Files
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

449 lines
15 KiB
C++

/*! \file */
/* ************************************************************************
* Copyright (C) 2024-2026 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.
*
* ************************************************************************ */
#include "hipsparse_bench_app.hpp"
#include "hipsparse_bench.hpp"
#include <chrono>
#include <fstream>
#include <random>
hipsparse_bench_app* hipsparse_bench_app::s_instance = nullptr;
hipsparse_bench_app_base::hipsparse_bench_app_base(int argc, char** argv)
: m_initial_argc(hipsparse_bench_app_base::save_initial_cmdline(argc, argv, &m_initial_argv))
, m_bench_cmdlines(argc, argv)
, m_bench_timing(m_bench_cmdlines.get_nsamples(), m_bench_cmdlines.get_nruns())
{};
hipsparseStatus_t hipsparse_bench_app_base::run_case(int isample, int irun, int argc, char** argv)
{
hipsparse_bench bench(argc, argv);
return bench.run();
}
hipsparseStatus_t hipsparse_bench_app_base::run_cases()
{
int sample_argc;
char** sample_argv = nullptr;
//
// Loop over cases.
//
int nruns = this->m_bench_cmdlines.get_nruns();
int nsamples = this->m_bench_cmdlines.get_nsamples();
if(is_stdout_disabled())
{
printf("// start benchmarking ... (nsamples = %d, nruns = %d)\n", nsamples, nruns);
}
for(int isample = 0; isample < nsamples; ++isample)
{
this->m_isample = isample;
//
// Add an item to collect data through hipsparse_record_timing
//
for(int irun = 0; irun < nruns; ++irun)
{
this->m_irun = irun;
//
// Get command line arguments, copy each time since it is mutable afterwards.
//
if(sample_argv == nullptr)
{
this->m_bench_cmdlines.get_argc(this->m_isample, sample_argc);
sample_argv = new char*[sample_argc];
}
this->m_bench_cmdlines.get(this->m_isample, sample_argc, sample_argv);
//
// Run the case.
//
hipsparseStatus_t status
= this->run_case(this->m_isample, this->m_irun, sample_argc, sample_argv);
if(status != HIPSPARSE_STATUS_SUCCESS)
{
std::cerr << "run_cases::run_case failed at line " << __LINE__ << std::endl;
return status;
}
if(is_stdout_disabled())
{
if((isample * nruns + irun) % 10 == 0)
{
fprintf(stdout,
"\r// %2.0f%%",
(double(isample * nruns + irun + 1) / double(nsamples * nruns)) * 100);
fflush(stdout);
}
}
}
}
if(is_stdout_disabled())
{
printf("\r// benchmarking done.\n");
}
if(sample_argv != nullptr)
{
delete[] sample_argv;
}
return HIPSPARSE_STATUS_SUCCESS;
};
hipsparse_bench_app::hipsparse_bench_app(int argc, char** argv)
: hipsparse_bench_app_base(argc, argv)
{
}
hipsparse_bench_app::~hipsparse_bench_app() {}
void hipsparse_bench_app::confidence_interval(const double alpha,
const int resize,
const int nboots,
const std::vector<double>& v,
double interval[2])
{
const size_t size = v.size();
static std::random_device dev;
static std::mt19937 rng(dev());
static std::uniform_int_distribution<std::mt19937::result_type> dist(0, size - 1);
std::vector<double> medians(nboots);
std::vector<double> resample(resize);
#define median_value(n__, s__) \
((n__ % 2 == 0) ? (s__[n__ / 2 - 1] + s__[n__ / 2]) * 0.5 : s__[n__ / 2])
for(int iboot = 0; iboot < nboots; ++iboot)
{
for(int i = 0; i < resize; ++i)
{
const int j = dist(rng);
resample[i] = v[j];
}
std::sort(resample.begin(), resample.end());
medians[iboot] = median_value(resize, resample);
}
std::sort(medians.begin(), medians.end());
interval[0] = medians[int(floor(nboots * 0.5 * (1.0 - alpha)))];
interval[1] = medians[int(ceil(nboots * (1.0 - 0.5 * (1.0 - alpha))))];
#undef median_value
}
void hipsparse_bench_app::export_item(std::ostream& out, hipsparse_bench_timing_t::item_t& item)
{
//
//
//
auto N = item.m_nruns;
if(N > 1)
{
const double alpha = 0.95;
std::sort(item.msec.begin(), item.msec.end());
std::sort(item.gflops.begin(), item.gflops.end());
std::sort(item.gbs.begin(), item.gbs.end());
double msec
= (N % 2 == 0) ? (item.msec[N / 2 - 1] + item.msec[N / 2]) * 0.5 : item.msec[N / 2];
double gflops = (N % 2 == 0) ? (item.gflops[N / 2 - 1] + item.gflops[N / 2]) * 0.5
: item.gflops[N / 2];
double gbs = (N % 2 == 0) ? (item.gbs[N / 2 - 1] + item.gbs[N / 2]) * 0.5 : item.gbs[N / 2];
double interval_msec[2], interval_gflops[2], interval_gbs[2];
int nboots = 200;
confidence_interval(alpha, 10, nboots, item.msec, interval_msec);
confidence_interval(alpha, 10, nboots, item.gflops, interval_gflops);
confidence_interval(alpha, 10, nboots, item.gbs, interval_gbs);
out << std::endl
<< " \"time\": [\"" << msec << "\", \"" << interval_msec[0] << "\", \""
<< interval_msec[1] << "\"]," << std::endl;
out << " \"flops\": [\"" << gflops << "\", \"" << interval_gflops[0] << "\", \""
<< interval_gflops[1] << "\"]," << std::endl;
out << " \"bandwidth\": [\"" << gbs << "\", \"" << interval_gbs[0] << "\", \""
<< interval_gbs[1] << "\"]";
if(!no_rawdata())
{
out << ",";
out << std::endl << " \"raw_legend\": \"" << item.outputs_legend << "\"";
out << ",";
out << std::endl << " \"raw_data\": \"" << item.outputs[0] << "\"";
}
}
else
{
out << std::endl
<< "\"time\": [\"" << item.msec[0] << "\", \"" << item.msec[0] << "\", \""
<< item.msec[0] << "\"]," << std::endl;
out << "\"flops\": [\"" << item.gflops[0] << "\", \"" << item.gflops[0] << "\", \""
<< item.gflops[0] << "\"]," << std::endl;
out << "\"bandwidth\": [\"" << item.gbs[0] << "\", \"" << item.gbs[0] << "\", \""
<< item.gbs[0] << "\"]";
if(!no_rawdata())
{
out << ",";
out << std::endl << " \"raw_legend\": \"" << item.outputs_legend << "\"";
out << ",";
out << std::endl << " \"raw_data\": ";
out << "\"" << item.outputs[0] << "\"";
out << "" << std::endl;
}
}
}
hipsparseStatus_t hipsparse_bench_app::export_file()
{
const char* ofilename = this->m_bench_cmdlines.get_ofilename();
if(ofilename == nullptr)
{
std::cerr << "//" << std::endl;
std::cerr << "// hipsparse_bench_app warning: no output filename has been specified,"
<< std::endl;
std::cerr << "// default output filename is 'a.json'." << std::endl;
std::cerr << "//" << std::endl;
ofilename = "a.json";
}
std::ofstream out(ofilename);
int sample_argc;
char* sample_argv[64];
hipsparseStatus_t status;
//
// Write header.
//
status = define_results_json(out);
if(status != HIPSPARSE_STATUS_SUCCESS)
{
std::cerr << "run_cases failed at line " << __LINE__ << std::endl;
return status;
}
//
// Loop over cases.
//
const size_t nsamples = m_bench_cmdlines.get_nsamples();
if(nsamples != m_bench_timing.size())
{
std::cerr << "incompatible sizes at line " << __LINE__ << " "
<< m_bench_cmdlines.get_nsamples() << " " << m_bench_timing.size() << std::endl;
if(m_bench_timing.size() == 0)
{
std::cerr << "No data has been harvested from running case" << std::endl;
}
exit(1);
}
for(size_t isample = 0; isample < nsamples; ++isample)
{
this->m_bench_cmdlines.get(isample, sample_argc, sample_argv);
this->define_case_json(out, isample, sample_argc, sample_argv);
out << "{ ";
{
this->export_item(out, this->m_bench_timing[isample]);
}
out << " }";
this->close_case_json(out, isample, sample_argc, sample_argv);
}
//
// Write footer.
//
status = this->close_results_json(out);
if(status != HIPSPARSE_STATUS_SUCCESS)
{
std::cerr << "run_cases failed at line " << __LINE__ << std::endl;
return status;
}
out.close();
return HIPSPARSE_STATUS_SUCCESS;
}
hipsparseStatus_t
hipsparse_bench_app::define_case_json(std::ostream& out, int isample, int argc, char** argv)
{
if(isample > 0)
out << "," << std::endl;
out << std::endl;
out << "{ \"cmdline\": \"";
out << argv[0];
for(int i = 1; i < argc; ++i)
out << " " << argv[i];
out << " \"," << std::endl;
out << " \"timing\": ";
return HIPSPARSE_STATUS_SUCCESS;
}
hipsparseStatus_t
hipsparse_bench_app::close_case_json(std::ostream& out, int isample, int argc, char** argv)
{
out << " }";
return HIPSPARSE_STATUS_SUCCESS;
}
hipsparseStatus_t hipsparse_bench_app::define_results_json(std::ostream& out)
{
out << "{" << std::endl;
auto end = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
char* str = std::ctime(&end_time);
for(int i = 0; i >= 0; ++i)
if(str[i] == '\n')
{
str[i] = '\0';
break;
}
out << "\"date\": \"" << str << "\"," << std::endl;
out << "\"hipSPARSE version\": \"" << hipsparse_get_version() << "\"," << std::endl;
//
// !!! To fix, not necessarily the gpu used from hipsparse_bench.
//
hipDeviceProp_t prop{};
std::ignore = hipGetDeviceProperties(&prop, 0);
gpu_config g(prop);
g.print_json(out);
out << std::endl << "\"cmdline\": \"" << this->m_initial_argv[0];
for(int i = 1; i < this->m_initial_argc; ++i)
{
out << " " << this->m_initial_argv[i];
}
out << "\"," << std::endl;
const size_t option_index_x = this->m_bench_cmdlines.get_option_index_x();
out << std::endl << "\"xargs\": \[";
for(int j = 0; j < this->m_bench_cmdlines.get_option_nargs(option_index_x); ++j)
{
auto arg = this->m_bench_cmdlines.get_option_arg(option_index_x, j);
if(j > 0)
out << ", ";
out << "\"" << arg << "\"";
}
out << "]," << std::endl;
out << std::endl << "\"yargs\":";
//
// Harvest expanded options.
//
std::vector<int> y_options_size;
std::vector<int> y_options_index;
for(int k = 0; k < this->m_bench_cmdlines.get_noptions(); ++k)
{
if(static_cast<size_t>(k) != option_index_x)
{
if(this->m_bench_cmdlines.get_option_nargs(k) > 1)
{
y_options_index.push_back(k);
y_options_size.push_back(this->m_bench_cmdlines.get_option_nargs(k));
}
}
}
const int num_y_options = y_options_index.size();
if(num_y_options > 0)
{
std::vector<std::vector<int>> indices(num_y_options);
for(int k = 0; k < num_y_options; ++k)
{
indices[k].resize(y_options_size[k], 0);
}
}
int nplots = this->m_bench_cmdlines.get_nsamples()
/ this->m_bench_cmdlines.get_option_nargs(option_index_x);
std::vector<std::string> plot_titles(nplots);
if(plot_titles.size() == 1)
{
plot_titles.push_back("");
}
else
{
int n = y_options_size[0];
auto argname0 = this->m_bench_cmdlines.get_option_name(y_options_index[0]);
for(int iplot = 0; iplot < nplots; ++iplot)
{
std::string title("");
int p = n;
{
int jref = iplot % p;
auto arg0 = this->m_bench_cmdlines.get_option_arg(y_options_index[0], jref);
if(argname0[1] == '-')
{
title += std::string(argname0 + 2) + std::string("=") + arg0;
}
else
{
title += std::string(argname0 + 1) + std::string("=") + arg0;
}
}
for(int k = 1; k < num_y_options; ++k)
{
int kref = iplot / p;
p *= this->m_bench_cmdlines.get_option_nargs(y_options_index[k]);
auto arg = this->m_bench_cmdlines.get_option_arg(y_options_index[k], kref);
auto argname = this->m_bench_cmdlines.get_option_name(y_options_index[k]);
if(argname[1] == '-')
{
title += std::string(",") + std::string(argname + 2) + std::string("=") + arg;
}
else
{
title += std::string(",") + std::string(argname + 1) + std::string("=") + arg;
}
}
plot_titles[iplot] = title;
}
}
out << "[";
{
out << "\"" << plot_titles[0] << "\"";
for(int iplot = 1; iplot < nplots; ++iplot)
out << ", \"" << plot_titles[iplot] << "\"";
}
out << "]," << std::endl << std::endl;
;
out << "\""
<< "results"
<< "\": [";
return HIPSPARSE_STATUS_SUCCESS;
}
hipsparseStatus_t hipsparse_bench_app::close_results_json(std::ostream& out)
{
out << "]" << std::endl;
out << "}" << std::endl;
return HIPSPARSE_STATUS_SUCCESS;
}