CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
827 lines
23 KiB
C++
827 lines
23 KiB
C++
// MIT License
|
|
//
|
|
// Copyright (c) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#ifndef ROCTHRUST_BENCHMARKS_BENCH_UTILS_CUSTOM_REPORTER_HPP_
|
|
#define ROCTHRUST_BENCHMARKS_BENCH_UTILS_CUSTOM_REPORTER_HPP_
|
|
|
|
// Utils
|
|
#include "common/types.hpp"
|
|
|
|
// Google Benchmark
|
|
#include <benchmark/benchmark.h>
|
|
|
|
// STL
|
|
#include <algorithm>
|
|
#include <cstdarg>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <numeric>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace bench_utils
|
|
{
|
|
|
|
template <class DstType, class SrcType>
|
|
bool IsType(const SrcType* src)
|
|
{
|
|
// Check if the src can be casted to the DstType
|
|
return dynamic_cast<const DstType*>(src) != nullptr;
|
|
}
|
|
|
|
static std::string FormatString(const char* msg, va_list args)
|
|
{
|
|
// we might need a second shot at this, so pre-emptivly make a copy
|
|
va_list args_cp;
|
|
va_copy(args_cp, args);
|
|
|
|
std::size_t size = 256;
|
|
char local_buff[256];
|
|
auto ret = vsnprintf(local_buff, size, msg, args_cp);
|
|
|
|
if (ret <= 0)
|
|
{
|
|
return {};
|
|
}
|
|
else if (static_cast<size_t>(ret) < size)
|
|
{
|
|
return local_buff;
|
|
}
|
|
|
|
// we did not provide a long enough buffer on our first attempt.
|
|
size = static_cast<size_t>(ret) + 1; // + 1 for the null byte
|
|
std::unique_ptr<char[]> buff(new char[size]);
|
|
ret = vsnprintf(buff.get(), size, msg, args);
|
|
return buff.get();
|
|
}
|
|
|
|
static std::string FormatString(const char* msg, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, msg);
|
|
auto tmp = FormatString(msg, args);
|
|
va_end(args);
|
|
return tmp;
|
|
}
|
|
|
|
std::string get_complexity(const benchmark::BigO& complexity)
|
|
{
|
|
switch (complexity)
|
|
{
|
|
case benchmark::oN:
|
|
return "N";
|
|
case benchmark::oNSquared:
|
|
return "N^2";
|
|
case benchmark::oNCubed:
|
|
return "N^3";
|
|
case benchmark::oLogN:
|
|
return "lgN";
|
|
case benchmark::oNLogN:
|
|
return "NlgN";
|
|
case benchmark::o1:
|
|
return "(1)";
|
|
default:
|
|
return "f(N)";
|
|
}
|
|
}
|
|
|
|
template <class Run>
|
|
double calculate_bw_utils(const Run& result)
|
|
{
|
|
// Calculates bandwith utilization in %
|
|
std::map<std::string, std::string>* global_context = benchmark::internal::GetGlobalContext();
|
|
if (global_context != nullptr)
|
|
{
|
|
for (const auto& keyval : *global_context)
|
|
{
|
|
if (keyval.first == "hdp_peak_global_mem_bus_bandwidth")
|
|
{
|
|
const double global_mem_bw = result.counters.at("bytes_per_second").value;
|
|
const double peak_global_bw = std::stod(keyval.second);
|
|
const double bw_util = 100. * global_mem_bw / peak_global_bw;
|
|
return bw_util;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// \brief Custom Google Benchmark reporter for formatting the benchmarks' report matching Thrust's.
|
|
///
|
|
/// This reporter is a ConsoleReporter that outputs:
|
|
/// - GPU Time: measured with events and registered as manual time.
|
|
/// - CPU Time: measured by Google Benchmark.
|
|
/// - Iterations: the number of kernel executions done on each benchmark run.
|
|
/// - GlobalMem BW: number of GB read and written to global memory per second of execution.
|
|
/// - BW util: percentage of the theoretical peak global memory bandwith utilised by the benchmark.
|
|
/// - Elements/s: number of G elements (G = 10**6) processed by second of execution.
|
|
/// - Labels: for extra labels. For instance, labels are used by the benchmarks generating random
|
|
/// input values to report the seed used.
|
|
///
|
|
/// Additionally, when the number of \p repetitions is greater than one, each benchmark run is
|
|
/// repeated \p repetitions times to measure the stability of results. In this case, the mean,
|
|
/// median, standard deviation (stddev) and coefficient of variation (cv) of the above-described
|
|
/// metrics are also reported after all the \p repetitions have ben run.
|
|
class CustomConsoleReporter : public benchmark::ConsoleReporter
|
|
{
|
|
private:
|
|
enum LogColor
|
|
{
|
|
COLOR_DEFAULT,
|
|
COLOR_RED,
|
|
COLOR_GREEN,
|
|
COLOR_YELLOW,
|
|
COLOR_BLUE,
|
|
COLOR_CYAN,
|
|
COLOR_WHITE
|
|
};
|
|
|
|
std::string get_log_color(LogColor&& color)
|
|
{
|
|
std::string s("unknown");
|
|
switch (color)
|
|
{
|
|
case COLOR_RED:
|
|
return "\033[31m";
|
|
case COLOR_GREEN:
|
|
return "\033[32m";
|
|
case COLOR_YELLOW:
|
|
return "\033[33m";
|
|
case COLOR_BLUE:
|
|
return "\033[34m";
|
|
case COLOR_CYAN:
|
|
return "\033[36m";
|
|
case COLOR_WHITE:
|
|
return "\033[37m";
|
|
default: // COLOR_DEFAULT
|
|
return "\033[0m";
|
|
}
|
|
}
|
|
|
|
void PrintColoredString(std::ostream& os, std::string color, std::string str, ...)
|
|
{
|
|
os << color;
|
|
va_list args;
|
|
va_start(args, str);
|
|
os << FormatString(str.data(), args);
|
|
va_end(args);
|
|
}
|
|
|
|
static std::string FormatTime(double time)
|
|
{
|
|
// Assuming the time is at max 9.9999e+99 and we have 10 digits for the
|
|
// number, we get 10-1(.)-1(e)-1(sign)-2(exponent) = 5 digits to print.
|
|
if (time > 9999999999 /*max 10 digit number*/)
|
|
{
|
|
return FormatString("%1.4e", time);
|
|
}
|
|
return FormatString("%10.3f", time);
|
|
}
|
|
|
|
public:
|
|
void PrintHeader(const Run& /*run*/)
|
|
{
|
|
// Assume run.counters (with elements processed and global mem reads/writes)
|
|
// will not be empty
|
|
std::string str = FormatString(
|
|
"%-*s %13s %15s %12s %16s %12s %14s %14s",
|
|
static_cast<int>(name_field_width_),
|
|
"Benchmark",
|
|
"GPU Time",
|
|
"CPU Time",
|
|
"Iterations",
|
|
"GlobalMem BW",
|
|
"BW util",
|
|
"GPU Noise",
|
|
"Elements/s");
|
|
std::string line = std::string(str.length(), '-');
|
|
GetOutputStream() << line << "\n" << str << "\n" << line << "\n";
|
|
}
|
|
|
|
void PrintRunData(const Run& result)
|
|
{
|
|
// Report benchmark name
|
|
auto& sout = GetOutputStream();
|
|
auto color = get_log_color((result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN);
|
|
|
|
PrintColoredString(sout, color, "%-*s ", name_field_width_, result.benchmark_name().c_str());
|
|
|
|
if (benchmark::internal::SkippedWithError == result.skipped)
|
|
{
|
|
PrintColoredString(sout, get_log_color(COLOR_RED), "ERROR OCCURRED: \'%s\'", result.skip_message.c_str());
|
|
PrintColoredString(sout, get_log_color(COLOR_DEFAULT), "\n");
|
|
return;
|
|
}
|
|
else if (benchmark::internal::SkippedWithMessage == result.skipped)
|
|
{
|
|
PrintColoredString(sout, get_log_color(COLOR_WHITE), "SKIPPED: \'%s\'", result.skip_message.c_str());
|
|
PrintColoredString(sout, get_log_color(COLOR_DEFAULT), "\n");
|
|
return;
|
|
}
|
|
|
|
// Report GPU and CPU time
|
|
const double gpu_time = result.GetAdjustedRealTime();
|
|
const double cpu_time = result.GetAdjustedCPUTime();
|
|
const std::string gpu_time_str = FormatTime(gpu_time);
|
|
const std::string cpu_time_str = FormatTime(cpu_time);
|
|
|
|
if (result.report_big_o)
|
|
{
|
|
std::string big_o = get_complexity(result.complexity);
|
|
PrintColoredString(
|
|
sout, get_log_color(COLOR_YELLOW), "%10.3f %-4s %10.3f %-4s ", gpu_time, big_o.c_str(), cpu_time, big_o.c_str());
|
|
}
|
|
else if (result.report_rms)
|
|
{
|
|
PrintColoredString(
|
|
sout, get_log_color(COLOR_YELLOW), "%10.3f %-4s %10.3f %-4s ", gpu_time * 100, "%", cpu_time * 100, "%");
|
|
}
|
|
else if (result.run_type != Run::RT_Aggregate || result.aggregate_unit == benchmark::StatisticUnit::kTime)
|
|
{
|
|
const char* timeLabel = GetTimeUnitString(result.time_unit);
|
|
PrintColoredString(
|
|
sout,
|
|
get_log_color(COLOR_YELLOW),
|
|
"%s %-4s %s %-4s ",
|
|
gpu_time_str.c_str(),
|
|
timeLabel,
|
|
cpu_time_str.c_str(),
|
|
timeLabel);
|
|
}
|
|
else
|
|
{
|
|
assert(result.aggregate_unit == benchmark::StatisticUnit::kPercentage);
|
|
PrintColoredString(
|
|
sout,
|
|
get_log_color(COLOR_YELLOW),
|
|
"%10.3f %-4s %10.3f %-4s ",
|
|
(100. * result.real_accumulated_time),
|
|
"%",
|
|
(100. * result.cpu_accumulated_time),
|
|
"%");
|
|
}
|
|
|
|
// Report iterations
|
|
if (!result.report_big_o && !result.report_rms)
|
|
{
|
|
PrintColoredString(sout, get_log_color(COLOR_CYAN), "%10lld", result.iterations);
|
|
}
|
|
|
|
// Report counters
|
|
std::string s;
|
|
const char* unit = "";
|
|
for (auto& c : result.counters)
|
|
{
|
|
std::size_t cNameLen = std::max(std::string::size_type(10), c.first.length());
|
|
if (c.first == "items_per_second")
|
|
{
|
|
// Print elements processed in G/s
|
|
if (result.run_type == Run::RT_Aggregate && result.aggregate_unit == benchmark::StatisticUnit::kPercentage)
|
|
{
|
|
s = FormatString("%.3f", 100. * c.second.value);
|
|
unit = "%";
|
|
}
|
|
else
|
|
{
|
|
s = FormatString("%.3f", c.second.value / 1e9);
|
|
unit = "G";
|
|
}
|
|
}
|
|
else if (c.first == "bytes_per_second")
|
|
{
|
|
// Print GlobalMem BW in GB/s
|
|
if (result.run_type == Run::RT_Aggregate && result.aggregate_unit == benchmark::StatisticUnit::kPercentage)
|
|
{
|
|
s = FormatString("%.3f", 100. * c.second.value);
|
|
unit = "%";
|
|
}
|
|
else
|
|
{
|
|
s = FormatString("%.3f", c.second.value / 1e9);
|
|
if (c.second.flags & benchmark::Counter::kIsRate)
|
|
{
|
|
unit = "GB/s";
|
|
}
|
|
}
|
|
PrintColoredString(sout, get_log_color(COLOR_DEFAULT), " %*s%s", cNameLen - strlen(unit), s.c_str(), unit);
|
|
|
|
// Print BW util
|
|
s = FormatString("%.2f", c.second.value);
|
|
const double bw_util = calculate_bw_utils(result);
|
|
if (bw_util >= 0)
|
|
{
|
|
s = FormatString("%.2f", bw_util);
|
|
unit = "%";
|
|
cNameLen = std::max(std::string::size_type(12), s.length());
|
|
}
|
|
}
|
|
else if (c.first == "gpu_noise")
|
|
{
|
|
if (result.run_type == Run::RT_Aggregate && result.aggregate_unit == benchmark::StatisticUnit::kPercentage)
|
|
{
|
|
s = FormatString("%.2f", 100. * c.second.value);
|
|
}
|
|
else
|
|
{
|
|
s = FormatString("%.2f", c.second.value);
|
|
}
|
|
unit = "%";
|
|
}
|
|
else
|
|
{
|
|
if (result.run_type == Run::RT_Aggregate && result.aggregate_unit == benchmark::StatisticUnit::kPercentage)
|
|
{
|
|
s = FormatString("%.2f", 100. * c.second.value);
|
|
unit = "%";
|
|
}
|
|
else
|
|
{
|
|
s = FormatString("%.2f", c.second.value / 1000.);
|
|
if (c.second.flags & benchmark::Counter::kIsRate)
|
|
{
|
|
unit = (c.second.flags & benchmark::Counter::kInvert) ? "s" : "/s";
|
|
}
|
|
}
|
|
}
|
|
|
|
PrintColoredString(sout, get_log_color(COLOR_DEFAULT), " %*s%s", cNameLen - strlen(unit), s.c_str(), unit);
|
|
}
|
|
|
|
// Print labels, that is, GPU noise
|
|
if (!result.report_label.empty())
|
|
{
|
|
std::string s = FormatString("%.2f", 100. * std::stod(result.report_label));
|
|
const char* unit = "%";
|
|
PrintColoredString(sout, get_log_color(COLOR_DEFAULT), " %13s%s", s.c_str(), unit);
|
|
}
|
|
|
|
PrintColoredString(sout, get_log_color(COLOR_DEFAULT), "\n");
|
|
}
|
|
|
|
// Called once for each group of benchmark runs, gives information about
|
|
// cpu-time, gpu-time, elements processed, global memory bw and the %
|
|
// the latter represents from the peak global bandwidth during the
|
|
// benchmark run. If the group of runs contained more than two entries
|
|
// then 'report' contains additional elements representing the mean,
|
|
// standard deviation and coefficient of variation of those runs.
|
|
// Additionally if this group of runs was the last in a family of
|
|
// benchmarks, 'reports' contains additional entries representing the
|
|
// asymptotic complexity and RMS of that benchmark family.
|
|
void ReportRuns(const std::vector<Run>& reports)
|
|
{
|
|
for (const auto& run : reports)
|
|
{
|
|
// Print the header if none was printed yet
|
|
bool print_header = !printed_header_;
|
|
if (print_header)
|
|
{
|
|
printed_header_ = true;
|
|
PrintHeader(run);
|
|
}
|
|
PrintRunData(run);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CustomJSONReporter : public benchmark::JSONReporter
|
|
{
|
|
private:
|
|
bool first_report_ = true;
|
|
|
|
std::string StrEscape(const std::string& s)
|
|
{
|
|
std::string tmp;
|
|
tmp.reserve(s.size());
|
|
for (char c : s)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '\b':
|
|
tmp += "\\b";
|
|
break;
|
|
case '\f':
|
|
tmp += "\\f";
|
|
break;
|
|
case '\n':
|
|
tmp += "\\n";
|
|
break;
|
|
case '\r':
|
|
tmp += "\\r";
|
|
break;
|
|
case '\t':
|
|
tmp += "\\t";
|
|
break;
|
|
case '\\':
|
|
tmp += "\\\\";
|
|
break;
|
|
case '"':
|
|
tmp += "\\\"";
|
|
break;
|
|
default:
|
|
tmp += c;
|
|
break;
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
std::string FormatKV(std::string const& key, std::string const& value)
|
|
{
|
|
return FormatString("\"%s\": \"%s\"", StrEscape(key).c_str(), StrEscape(value).c_str());
|
|
}
|
|
|
|
std::string FormatKV(std::string const& key, const char* value)
|
|
{
|
|
return FormatString("\"%s\": \"%s\"", StrEscape(key).c_str(), StrEscape(value).c_str());
|
|
}
|
|
|
|
std::string FormatKV(std::string const& key, bool value)
|
|
{
|
|
return FormatString("\"%s\": %s", StrEscape(key).c_str(), value ? "true" : "false");
|
|
}
|
|
|
|
std::string FormatKV(std::string const& key, int64_t value)
|
|
{
|
|
std::stringstream ss;
|
|
ss << '"' << StrEscape(key) << "\": " << value;
|
|
return ss.str();
|
|
}
|
|
|
|
std::string FormatKV(std::string const& key, double value)
|
|
{
|
|
std::stringstream ss;
|
|
ss << '"' << StrEscape(key) << "\": ";
|
|
|
|
if (std::isnan(value))
|
|
{
|
|
ss << (value < 0 ? "-" : "") << "NaN";
|
|
}
|
|
else if (std::isinf(value))
|
|
{
|
|
ss << (value < 0 ? "-" : "") << "Infinity";
|
|
}
|
|
else
|
|
{
|
|
const auto max_digits10 = std::numeric_limits<decltype(value)>::max_digits10;
|
|
const auto max_fractional_digits10 = max_digits10 - 1;
|
|
ss << std::scientific << std::setprecision(max_fractional_digits10) << value;
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
public:
|
|
void ReportRuns(std::vector<Run> const& reports)
|
|
{
|
|
if (reports.empty())
|
|
{
|
|
return;
|
|
}
|
|
std::string indent(4, ' ');
|
|
std::ostream& out = GetOutputStream();
|
|
if (!first_report_)
|
|
{
|
|
out << ",\n";
|
|
}
|
|
first_report_ = false;
|
|
|
|
for (auto it = reports.begin(); it != reports.end(); ++it)
|
|
{
|
|
out << indent << "{\n";
|
|
PrintRunData(*it);
|
|
out << indent << '}';
|
|
auto it_cp = it;
|
|
if (++it_cp != reports.end())
|
|
{
|
|
out << ",\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrintRunData(Run const& run)
|
|
{
|
|
std::string indent(6, ' ');
|
|
std::ostream& out = GetOutputStream();
|
|
|
|
auto output_format = [this, &out, &indent](const std::string& label, auto val, bool start_endl = true) {
|
|
if (start_endl)
|
|
{
|
|
out << ",\n";
|
|
}
|
|
out << indent << FormatKV(label, val);
|
|
};
|
|
|
|
output_format("name", run.benchmark_name(), false);
|
|
output_format("family_index", run.family_index);
|
|
output_format("per_family_instance_index", run.per_family_instance_index);
|
|
output_format("run_name", run.run_name.str());
|
|
output_format("run_type", [&run]() -> const char* {
|
|
switch (run.run_type)
|
|
{
|
|
case BenchmarkReporter::Run::RT_Iteration:
|
|
return "iteration";
|
|
case BenchmarkReporter::Run::RT_Aggregate:
|
|
return "aggregate";
|
|
}
|
|
BENCHMARK_UNREACHABLE();
|
|
}());
|
|
output_format("repetitions", run.repetitions);
|
|
if (run.run_type != BenchmarkReporter::Run::RT_Aggregate)
|
|
{
|
|
output_format("repetition_index", run.repetition_index);
|
|
}
|
|
output_format("threads", run.threads);
|
|
if (run.run_type == BenchmarkReporter::Run::RT_Aggregate)
|
|
{
|
|
output_format("aggregate_name", run.aggregate_name);
|
|
output_format("aggregate_unit", [&run]() -> const char* {
|
|
switch (run.aggregate_unit)
|
|
{
|
|
case benchmark::StatisticUnit::kTime:
|
|
return "time";
|
|
case benchmark::StatisticUnit::kPercentage:
|
|
return "percentage";
|
|
}
|
|
BENCHMARK_UNREACHABLE();
|
|
}());
|
|
}
|
|
if (benchmark::internal::SkippedWithError == run.skipped)
|
|
{
|
|
output_format("error_occurred", true);
|
|
output_format("error_message", run.skip_message);
|
|
}
|
|
else if (benchmark::internal::SkippedWithMessage == run.skipped)
|
|
{
|
|
output_format("skipped", true);
|
|
output_format("skip_message", run.skip_message);
|
|
}
|
|
if (!run.report_big_o && !run.report_rms)
|
|
{
|
|
output_format("iterations", run.iterations);
|
|
if (run.run_type != Run::RT_Aggregate || run.aggregate_unit == benchmark::StatisticUnit::kTime)
|
|
{
|
|
output_format("gpu_time", run.GetAdjustedRealTime());
|
|
output_format("cpu_time", run.GetAdjustedCPUTime());
|
|
}
|
|
else
|
|
{
|
|
assert(run.aggregate_unit == benchmark::StatisticUnit::kPercentage);
|
|
output_format("gpu_time", run.real_accumulated_time);
|
|
output_format("cpu_time", run.cpu_accumulated_time);
|
|
}
|
|
output_format("time_unit", GetTimeUnitString(run.time_unit));
|
|
}
|
|
else if (run.report_big_o)
|
|
{
|
|
output_format("cpu_coefficient", run.GetAdjustedCPUTime());
|
|
output_format("gpu_coefficient", run.GetAdjustedRealTime());
|
|
output_format("big_o", get_complexity(run.complexity));
|
|
output_format("time_unit", GetTimeUnitString(run.time_unit));
|
|
}
|
|
else if (run.report_rms)
|
|
{
|
|
output_format("rms", run.GetAdjustedCPUTime());
|
|
}
|
|
|
|
for (auto& c : run.counters)
|
|
{
|
|
if (c.first == "items_per_second")
|
|
{
|
|
// Report same name as console reporter
|
|
output_format("elements_per_second", c.second);
|
|
}
|
|
else if (c.first == "bytes_per_second")
|
|
{
|
|
// Report same name as console reporter
|
|
output_format("global_mem_bw", c.second);
|
|
const double util_bw = calculate_bw_utils(run);
|
|
if (util_bw >= 0)
|
|
{
|
|
output_format("util_bw", util_bw);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
output_format(c.first, c.second);
|
|
}
|
|
}
|
|
|
|
const benchmark::MemoryManager::Result memory_result = run.memory_result;
|
|
output_format("allocs_per_iter", run.allocs_per_iter);
|
|
output_format("max_bytes_used", memory_result.max_bytes_used);
|
|
|
|
if (memory_result.total_allocated_bytes != benchmark::MemoryManager::TombstoneValue)
|
|
{
|
|
output_format("total_allocated_bytes", memory_result.total_allocated_bytes);
|
|
}
|
|
|
|
if (memory_result.net_heap_growth != benchmark::MemoryManager::TombstoneValue)
|
|
{
|
|
output_format("net_heap_growth", memory_result.net_heap_growth);
|
|
}
|
|
|
|
if (!run.report_label.empty())
|
|
{
|
|
output_format("label", run.report_label);
|
|
}
|
|
out << '\n';
|
|
}
|
|
};
|
|
|
|
BENCHMARK_DISABLE_DEPRECATED_WARNING
|
|
|
|
class CustomCSVReporter : public benchmark::CSVReporter
|
|
{
|
|
private:
|
|
bool printed_header_ = false;
|
|
|
|
std::vector<std::string> elements = {
|
|
"name",
|
|
"iterations",
|
|
"gpu_time",
|
|
"cpu_time",
|
|
"time_unit",
|
|
"global_mem_bw",
|
|
"util_bw",
|
|
"elements_per_second",
|
|
"gpu_noise",
|
|
"label",
|
|
"error_occurred",
|
|
"error_message"};
|
|
|
|
std::string CsvEscape(const std::string& s)
|
|
{
|
|
std::string tmp;
|
|
tmp.reserve(s.size() + 2);
|
|
for (char c : s)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '"':
|
|
tmp += "\"\"";
|
|
break;
|
|
default:
|
|
tmp += c;
|
|
break;
|
|
}
|
|
}
|
|
return '"' + tmp + '"';
|
|
}
|
|
|
|
public:
|
|
void PrintHeader(const Run& /*run*/)
|
|
{
|
|
std::string str = "";
|
|
bool first = true;
|
|
for (auto element : elements)
|
|
{
|
|
if (first)
|
|
{
|
|
first = false;
|
|
}
|
|
else
|
|
{
|
|
str += ",";
|
|
}
|
|
str += element;
|
|
}
|
|
GetOutputStream() << str << "\n";
|
|
}
|
|
|
|
void PrintRunData(const Run& result)
|
|
{
|
|
// Report benchmark name
|
|
auto& sout = GetOutputStream();
|
|
|
|
if (result.skipped)
|
|
{
|
|
sout << std::string(elements.size() - 3, ',');
|
|
sout << std::boolalpha << (benchmark::internal::SkippedWithError == result.skipped) << ",";
|
|
sout << CsvEscape(result.skip_message) << "\n";
|
|
return;
|
|
}
|
|
|
|
sout << CsvEscape(result.benchmark_name()) << ",";
|
|
|
|
if (!result.report_big_o && !result.report_rms)
|
|
{
|
|
sout << result.iterations;
|
|
}
|
|
sout << ",";
|
|
|
|
sout << result.GetAdjustedRealTime() << ",";
|
|
sout << result.GetAdjustedCPUTime() << ",";
|
|
|
|
if (result.report_big_o)
|
|
{
|
|
sout << get_complexity(result.complexity);
|
|
}
|
|
else if (!result.report_rms)
|
|
{
|
|
sout << benchmark::GetTimeUnitString(result.time_unit);
|
|
}
|
|
sout << ",";
|
|
|
|
if (result.counters.find("bytes_per_second") != result.counters.end())
|
|
{
|
|
sout << result.counters.at("bytes_per_second");
|
|
}
|
|
sout << ",";
|
|
|
|
if (result.counters.find("bytes_per_second") != result.counters.end())
|
|
{
|
|
const double bw_util = calculate_bw_utils(result);
|
|
if (bw_util >= 0)
|
|
{
|
|
sout << bw_util;
|
|
}
|
|
}
|
|
sout << ",";
|
|
|
|
if (result.counters.find("items_per_second") != result.counters.end())
|
|
{
|
|
sout << result.counters.at("items_per_second");
|
|
}
|
|
sout << ",";
|
|
|
|
if (result.counters.find("gpu_noise") != result.counters.end())
|
|
{
|
|
sout << result.counters.at("gpu_noise");
|
|
}
|
|
sout << ",";
|
|
|
|
if (!result.report_label.empty())
|
|
{
|
|
sout << CsvEscape(result.report_label);
|
|
}
|
|
|
|
sout << ",,\n";
|
|
}
|
|
|
|
void ReportRuns(const std::vector<Run>& reports)
|
|
{
|
|
for (const auto& run : reports)
|
|
{
|
|
// Print the header if none was printed yet
|
|
bool print_header = !printed_header_;
|
|
if (print_header)
|
|
{
|
|
printed_header_ = true;
|
|
PrintHeader(run);
|
|
}
|
|
PrintRunData(run);
|
|
}
|
|
}
|
|
};
|
|
|
|
benchmark::BenchmarkReporter* ChooseCustomReporter()
|
|
{
|
|
// benchmark::BenchmarkReporter is polymorphic as it has a virtual
|
|
// function which allows us to use dynamic_cast to detect the derived type.
|
|
using PtrType = benchmark::BenchmarkReporter*;
|
|
PtrType default_display_reporter = benchmark::CreateDefaultDisplayReporter();
|
|
|
|
if (IsType<benchmark::CSVReporter>(default_display_reporter))
|
|
{
|
|
return PtrType(new CustomCSVReporter);
|
|
}
|
|
else if (IsType<benchmark::JSONReporter>(default_display_reporter))
|
|
{
|
|
return PtrType(new CustomJSONReporter);
|
|
}
|
|
else if (IsType<benchmark::ConsoleReporter>(default_display_reporter))
|
|
{
|
|
return PtrType(new CustomConsoleReporter);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
BENCHMARK_RESTORE_DEPRECATED_WARNING
|
|
|
|
} // namespace bench_utils
|
|
#endif // ROCTHRUST_BENCHMARKS_BENCH_UTILS_CUSTOM_REPORTER_HPP_
|