Files
TheRock/rocm-libraries/projects/rocblas/clients/common/client_utility.cpp
T
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

709 lines
24 KiB
C++

/* ************************************************************************
* Copyright (C) 2018-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 cop-
* ies 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 IM-
* PLIED, 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 CONNE-
* CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* ************************************************************************ */
#ifdef WIN32
#include <windows.h>
//
#include <random>
#endif
#include "../../library/src/include/handle.hpp"
#include "client_utility.hpp"
#include "d_vector.hpp"
#include "gtest_helpers.hpp"
#include <chrono>
#include <cstdlib>
#include <new>
#include <stdexcept>
#include <stdlib.h>
#include <thread>
#ifdef BUILD_WITH_HIPBLASLT
#include <hipblaslt/hipblaslt-ext.hpp>
#endif
#ifdef WIN32
#define strcasecmp(A, B) _stricmp(A, B)
#define setenv(A, B, C) _putenv_s(A, B)
#define unsetenv(A) _putenv_s(A, "")
#ifdef __cpp_lib_filesystem
#include <filesystem>
namespace fs = std::filesystem;
#else
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
// Not WIN32
#else
#include <fcntl.h>
#include <unistd.h>
#endif
void rocblas_client_init()
{
#ifndef WIN32
auto* ld_library_path_override = getenv("LD_LIBRARY_PATH");
if(ld_library_path_override)
{
rocblas_cout << "rocBLAS warning: LD_LIBRARY_PATH override may use incompatible rocblas "
<< std::endl;
}
#endif
// Warn users if using older reference library
print_reference_lib_warning();
}
void rocblas_client_shutdown() {}
void get_version_string(std::string& str)
{
size_t size;
rocblas_get_version_string_size(&size);
str.resize(size - 1, '\0');
rocblas_get_version_string((char*)str.data(), size);
}
void print_rocblas_version_string()
{
std::string rocblas_version;
get_version_string(rocblas_version);
rocblas_cout << "rocBLAS version: " << rocblas_version << std::endl;
print_rocblas_client_commit_hashes();
}
#define TOSTR2(s) #s
#define TOSTR(s) TOSTR2(s)
void print_reference_lib_warning()
{
#ifdef ROCBLAS_REFERENCE_LIB
rocblas_cout << "rocBLAS info: Using reference library '" << TOSTR(ROCBLAS_REFERENCE_LIB) << "'"
<< std::endl;
#endif
// prints a warning to cout if the recommended reference library isn't used
#ifdef ROCBLAS_REFERENCE_LIB_WARN
rocblas_cout << "rocBLAS warning: Reference library may not support 64-bit input arguments. "
"If running a test suite, please use "
<< "--gtest_filter=-*stress* to avoid 64-bit test failures.\n";
#endif
}
void print_asan_kernel_warning(const char* program_name)
{
#if defined(__SANITIZE_ADDRESS__) || (defined(__has_feature) && __has_feature(address_sanitizer))
rocblas_cout << program_name << " WARNING: AddressSanitizer build" << std::endl;
#endif
}
// Print rocBLAS and Tensile commit hashes
void print_rocblas_client_commit_hashes()
{
static const char* rocblas_tensile_commit_hash[] = {ROCBLAS_TENSILE_COMMIT_ID};
rocblas_cout << "rocBLAS-commit-hash: " << rocblas_tensile_commit_hash[0] << std::endl;
#if BUILD_WITH_TENSILE
rocblas_cout << "Tensile-commit-hash: " << rocblas_tensile_commit_hash[1] << std::endl;
#else
rocblas_cout << "Tensile-commit-hash: N/A, as rocBLAS was built without Tensile" << std::endl;
#endif
#ifdef BUILD_WITH_HIPBLASLT
rocblas_cout << "hipBLASLt version: " << TOSTR(HIPBLASLT_VERSION_MAJOR) << "."
<< TOSTR(HIPBLASLT_VERSION_MINOR) << "." << TOSTR(HIPBLASLT_VERSION_PATCH)
<< " commit-hash: " << TOSTR(HIPBLASLT_VERSION_TWEAK) << std::endl;
#else
rocblas_cout << "hipBLASLt: N/A, as rocBLAS was built without hipBLASLt" << std::endl;
#endif
size_t size;
rocblas_get_commit_hash_string_size(&size);
std::string hash(size - 1, '\0');
rocblas_get_commit_hash_string((char*)hash.data(), size);
if(strcmp(rocblas_tensile_commit_hash[0], hash.data()))
{
rocblas_cout
<< "rocBLAS warning: client rocblas commit differs from library rocblas commit: "
<< hash << std::endl;
}
}
#undef TOSTR
#undef TOSTR2
bool rocblas_file_exists(const char* path)
{
#ifdef WIN32
fs::path file_path(path);
return fs::exists(file_path);
#else
return (access(path, F_OK) != -1);
#endif
}
/* ============================================================================================ */
// Return path of this executable
std::string rocblas_exepath()
{
#ifdef WIN32
// for now not building with wide chars
// wchar_t wpath[MAX_PATH + 1] = {0};
// GetModuleFileNameW(NULL, wpath, MAX_PATH + 1);
// std::vector<wchar_t> result(MAX_PATH + 1);
std::vector<TCHAR> result(MAX_PATH + 1);
// Ensure result is large enough to accommodate the path
DWORD length = 0;
for(;;)
{
length = GetModuleFileNameA(nullptr, result.data(), result.size());
if(length < result.size() - 1)
{
result.resize(length + 1);
break;
}
result.resize(result.size() * 2);
}
// std::wstring wspath(result.data());
// fs::path exepath(wspath.begin(), wspath.end());
fs::path exepath(result.begin(), result.end());
exepath = exepath.remove_filename();
// Add trailing "/" to exepath if required
exepath += exepath.empty() ? "" : "/";
return exepath.string();
#else
std::string pathstr;
char* path = realpath("/proc/self/exe", 0);
if(path)
{
char* p = strrchr(path, '/');
if(p)
{
p[1] = 0;
pathstr = path;
}
free(path);
}
return pathstr;
#endif
}
/* ============================================================================================ */
// Temp directory rooted random path
std::string rocblas_tempname()
{
#ifdef WIN32
// Generate "/tmp/rocblas-XXXXXX" like file name
const std::string alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv";
int stringlength = alphanum.length() - 1;
std::string uniquestr = "rocblas-";
for(auto n : {0, 1, 2, 3, 4, 5})
uniquestr += alphanum.at(rand() % stringlength);
fs::path tmpname = fs::temp_directory_path() / uniquestr;
return tmpname.string();
#else
char tmp[] = "/tmp/rocblas-XXXXXX";
int fd = mkostemp(tmp, O_CLOEXEC);
if(fd == -1)
{
dprintf(STDERR_FILENO, "Cannot open temporary file: %m\n");
exit(EXIT_FAILURE);
}
return std::string(tmp);
#endif
}
/* ============================================================================================ */
/* memory allocation requirements :*/
/*! \brief Compute strided batched matrix allocation size allowing for strides smaller than full matrix */
size_t
strided_batched_matrix_size(int rows, int cols, int lda, rocblas_stride stride, int batch_count)
{
size_t size = size_t(lda) * cols;
if(batch_count > 1)
{
// for cases where batch_count strides may not exceed full matrix size use full matrix size
// e.g. row walking a larger matrix we just use full matrix size
size_t size_strides = (batch_count - 1) * stride;
size += size < size_strides + (cols - 1) * size_t(lda) + rows ? size_strides : 0;
}
return size;
}
/* ============================================================================================ */
/* timing:*/
/*! \brief CPU Timer(in microsecond): synchronize with the default device and return wall time */
double get_time_us_sync_device(void)
{
THROW_IF_HIP_ERROR(hipDeviceSynchronize());
auto now = std::chrono::steady_clock::now();
// now.time_since_epoch() is the duration since epoch
// which is converted to microseconds
auto duration
= std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
return (static_cast<double>(duration));
};
/*! \brief CPU Timer(in microsecond): synchronize with given queue/stream and return wall time */
double get_time_us_sync(hipStream_t stream)
{
static bool oldStreamSync = [&] {
// set old mode if env variable is set
const char* str_stream_sync
= getenv("ROCBLAS_BENCH_STREAM_SYNC"); // windows getenv is fine for static init
if(str_stream_sync)
{
return (strncmp(str_stream_sync, "1", 1) == 0 ? true : false);
}
return false;
}();
if(oldStreamSync)
{
THROW_IF_HIP_ERROR(hipStreamSynchronize(stream));
auto now = std::chrono::steady_clock::now();
// now.time_since_epoch() is the duration since epoch
// which is converted to microseconds
auto duration
= std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
return (static_cast<double>(duration));
}
else
{
// avoid any stream synchronization overhead on timing
static thread_local hipEvent_t start{nullptr};
static thread_local hipEvent_t stop{nullptr};
if(!start)
{
THROW_IF_HIP_ERROR(hipEventCreate(&start));
THROW_IF_HIP_ERROR(hipEventCreate(&stop));
THROW_IF_HIP_ERROR(hipEventRecord(start, stream));
return 0.0;
}
else
{
THROW_IF_HIP_ERROR(hipEventRecord(stop, stream));
THROW_IF_HIP_ERROR(hipEventSynchronize(stop));
float milliseconds = 0;
THROW_IF_HIP_ERROR(hipEventElapsedTime(&milliseconds, start, stop));
THROW_IF_HIP_ERROR(hipEventDestroy(stop));
THROW_IF_HIP_ERROR(hipEventDestroy(start));
start = nullptr; // reset start event for next use
stop = nullptr;
return milliseconds * 1000.0; // convert to microseconds
}
}
};
/*! \brief CPU Timer(in microsecond): no GPU synchronization */
double get_time_us_no_sync(void)
{
auto now = std::chrono::steady_clock::now();
// now.time_since_epoch() is the duration since epoch
// which is converted to microseconds
auto duration
= std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
return (static_cast<double>(duration));
};
/* ============================================================================================ */
/* device query and print out their ID and name; return number of compute-capable devices. */
rocblas_int query_device_property()
{
int device_count;
rocblas_status status = (rocblas_status)hipGetDeviceCount(&device_count);
if(status != rocblas_status_success)
{
rocblas_cerr << "Query device error: cannot get device count" << std::endl;
return -1;
}
else
{
rocblas_cout << "Query device success: there are " << device_count << " devices"
<< std::endl;
}
for(rocblas_int i = 0;; i++)
{
rocblas_cout
<< "-------------------------------------------------------------------------------"
<< std::endl;
if(i >= device_count)
break;
hipDeviceProp_t props;
rocblas_status status = (rocblas_status)hipGetDeviceProperties(&props, i);
if(status != rocblas_status_success)
{
rocblas_cerr << "Query device error: cannot get device ID " << i << "'s property"
<< std::endl;
}
else
{
char buf[320];
snprintf(
buf,
sizeof(buf),
"Device ID %d : %s %s\n"
"with %3.1f GB memory, max. SCLK %d MHz, max. MCLK %d MHz, memoryBusWidth %d "
"Bytes, compute capability "
"%d.%d\n"
"maxGridDimX %d, sharedMemPerBlock %3.1f KB, maxThreadsPerBlock %d, warpSize %d\n",
i,
props.name,
props.gcnArchName,
props.totalGlobalMem / 1e9,
(int)(props.clockRate / 1000),
(int)(props.memoryClockRate / 1000),
(int)(props.memoryBusWidth / 8),
props.major,
props.minor,
props.maxGridSize[0],
props.sharedMemPerBlock / 1e3,
props.maxThreadsPerBlock,
props.warpSize);
rocblas_cout << buf;
}
}
return device_count;
}
/* set current device to device_id */
void set_device(rocblas_int device_id)
{
rocblas_status status = (rocblas_status)hipSetDevice(device_id);
if(status != rocblas_status_success)
{
rocblas_cerr << "Set device error: cannot set device ID " << device_id
<< ", there may not be such device ID" << std::endl;
}
}
/*********************************************
* callback function
*********************************************/
thread_local std::unique_ptr<std::function<void(rocblas_handle)>> t_set_stream_callback;
/*****************
* local handles *
*****************/
rocblas_local_handle::rocblas_local_handle()
{
auto status = rocblas_create_handle(&m_handle);
if(status != rocblas_status_success)
throw std::runtime_error(rocblas_status_to_string(status));
#ifdef GOOGLE_TEST
if(t_set_stream_callback)
{
(*t_set_stream_callback)(m_handle);
t_set_stream_callback.reset();
}
#endif
}
rocblas_local_handle::rocblas_local_handle(const Arguments& arg)
{
if(arg.use_hipblaslt >= 0)
{
auto hipblaslt_env = getenv("ROCBLAS_USE_HIPBLASLT");
if(hipblaslt_env)
m_hipblaslt_saved_status = std::string(hipblaslt_env);
m_hipblaslt_env_set = true;
setenv("ROCBLAS_USE_HIPBLASLT", std::to_string(arg.use_hipblaslt).c_str(), true);
}
if(arg.graph_test)
{
auto stream_order_env = getenv("ROCBLAS_STREAM_ORDER_ALLOC");
if(stream_order_env)
m_stream_order_saved_status = std::string(stream_order_env);
m_stream_order_env_set = true;
setenv("ROCBLAS_STREAM_ORDER_ALLOC", "1", true);
}
auto status = rocblas_create_handle(&m_handle);
if(status != rocblas_status_success)
throw std::runtime_error(rocblas_status_to_string(status));
#ifdef GOOGLE_TEST
if(t_set_stream_callback)
{
(*t_set_stream_callback)(m_handle);
t_set_stream_callback.reset();
}
#endif
// Set the atomics mode
status = rocblas_set_atomics_mode(m_handle, arg.atomics_mode);
// The check_numerics mode conditional defeat with "rocblas_check_numerics_mode_no_check"
// Defeat check numerics when initializing any data with NaN with due to alpha or beta having NaN flags,
// or if the arg.initalization is rocblas_initialization::denorm or rocblas_initialization::denorm2
// The denorm initialization are sometimes used when testing the gemm_ex and gemm_strided_batched_ex functions
if(rocblas_isnan(arg.alpha) || rocblas_isnan(arg.beta)
|| arg.initialization == rocblas_initialization::denorm
|| arg.initialization == rocblas_initialization::denorm2)
{
m_handle->check_numerics = rocblas_check_numerics_mode_no_check;
}
else if(m_handle->check_numerics != rocblas_check_numerics_mode_no_check)
{
// bypass denorm reporting for f16 gemms, to be analyzed further
if(arg.a_type == rocblas_datatype_f16_r && m_handle->getArchMajor() == 11
&& strstr(arg.function, "gemm"))
{
m_handle->check_numerics = static_cast<rocblas_check_numerics_mode>(
m_handle->check_numerics | rocblas_check_numerics_mode_only_nan_inf);
}
}
if(status == rocblas_status_success)
{
// If the test specifies user allocated workspace, allocate and use it
if(arg.user_allocated_workspace)
{
if((hipMalloc)(&m_memory, arg.user_allocated_workspace) != hipSuccess)
throw std::bad_alloc();
status = rocblas_set_workspace(m_handle, m_memory, arg.user_allocated_workspace);
}
}
// memory guard control, with multi-threading should not change values across threads
d_vector_set_pad_length(arg.pad);
if(status != rocblas_status_success)
throw std::runtime_error(rocblas_status_to_string(status));
status = rocblas_set_math_mode(m_handle, rocblas_math_mode(arg.math_mode));
if(status != rocblas_status_success)
throw std::runtime_error(rocblas_status_to_string(status));
}
rocblas_local_handle::~rocblas_local_handle()
{
if(m_graph_stream)
rocblas_stream_end_capture();
if(m_memory)
{
PRINT_IF_HIP_ERROR((hipFree)(m_memory));
}
if(m_hipblaslt_env_set)
{
setenv("ROCBLAS_USE_HIPBLASLT", m_hipblaslt_saved_status.c_str(), true);
}
rocblas_destroy_handle(m_handle);
if(m_stream_order_env_set)
{
setenv("ROCBLAS_STREAM_ORDER_ALLOC", m_stream_order_saved_status.c_str(), true);
}
}
void rocblas_local_handle::rocblas_stream_begin_capture()
{
if(m_graph_stream)
throw std::runtime_error("recursive rocblas_stream_begin_capture");
CHECK_ROCBLAS_ERROR(rocblas_get_stream(m_handle, &m_old_stream));
CHECK_HIP_ERROR(hipStreamSynchronize(m_old_stream));
// set via env
// m_handle->set_stream_order_memory_allocation(true);
CHECK_HIP_ERROR(hipStreamCreate(&m_graph_stream));
CHECK_ROCBLAS_ERROR(rocblas_set_stream(m_handle, m_graph_stream));
// BEGIN GRAPH CAPTURE
CHECK_HIP_ERROR(hipStreamBeginCapture(m_graph_stream, hipStreamCaptureModeGlobal));
}
void rocblas_local_handle::rocblas_stream_end_capture()
{
hipGraph_t graph;
hipGraphExec_t instance;
// END GRAPH CAPTURE
CHECK_HIP_ERROR(hipStreamEndCapture(m_graph_stream, &graph));
CHECK_HIP_ERROR(hipGraphInstantiate(&instance, graph, NULL, NULL, 0));
CHECK_HIP_ERROR(hipGraphDestroy(graph));
CHECK_HIP_ERROR(hipGraphLaunch(instance, m_graph_stream));
CHECK_HIP_ERROR(hipStreamSynchronize(m_graph_stream));
CHECK_HIP_ERROR(hipGraphExecDestroy(instance));
CHECK_ROCBLAS_ERROR(rocblas_set_stream(m_handle, m_old_stream));
CHECK_HIP_ERROR(hipStreamDestroy(m_graph_stream));
m_graph_stream = nullptr;
// set via env
// m_handle->set_stream_order_memory_allocation(false);
}
void rocblas_parallel_initialize_thread(int id, size_t& memory_used)
{
size_t before_init, after_init, total_memory;
CHECK_HIP_ERROR(hipSetDevice(id));
CHECK_HIP_ERROR(hipMemGetInfo(&before_init, &total_memory));
rocblas_initialize();
CHECK_HIP_ERROR(hipMemGetInfo(&after_init, &total_memory));
memory_used = before_init - after_init;
}
/*!
* Initialize rocBLAS for the requested number of HIP devices
* and report the time taken to complete the initialization.
* This is to avoid costly startup time at the first call on
* that device. Internal use for benchmark & testing.
* Initializes devices indexed from 0 to parallel_devices-1.
* If parallel_devices is 1, hipSetDevice should be called
* before calling this function.
*/
void rocblas_parallel_initialize(int parallel_devices)
{
auto thread = std::make_unique<std::thread[]>(parallel_devices);
std::vector<size_t> init_memory(parallel_devices);
// Store the start timepoint of rocblas initialize
auto start_time = std::chrono::steady_clock::now();
if(parallel_devices == 1)
{
size_t before_init, after_init, total_memory;
CHECK_HIP_ERROR(hipMemGetInfo(&before_init, &total_memory));
rocblas_initialize();
CHECK_HIP_ERROR(hipMemGetInfo(&after_init, &total_memory));
init_memory[0] = before_init - after_init;
}
else
{
for(int id = 0; id < parallel_devices; ++id)
thread[id]
= std::thread(rocblas_parallel_initialize_thread, id, std::ref(init_memory[id]));
for(int id = 0; id < parallel_devices; ++id)
thread[id].join();
}
// Store the end timepoint of rocblas initialize
auto end_time = std::chrono::steady_clock::now();
// Compute the time taken to load the Tensile kernels (in milliseconds).
auto init_time_in_ms
= std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
rocblas_cout << "\nrocBLAS info: Time taken to complete rocBLAS library initialization is "
<< init_time_in_ms << " milliseconds." << std::endl;
// Calculate average initialization time per GPU
auto avg_init_time_in_ms = init_time_in_ms / parallel_devices;
if(parallel_devices > 1)
{
rocblas_cout
<< "\nrocBLAS info: Average time taken to complete rocBLAS library initialization "
"per device is "
<< avg_init_time_in_ms << " milliseconds." << std::endl;
}
// If average initialization time exceeds the max duration, display the following info message.
constexpr static int max_duration = 5000;
if(avg_init_time_in_ms > max_duration)
rocblas_cerr << "\nrocBLAS info: average time to initialize each device exceeded the max "
"duration of "
<< max_duration << " milliseconds. Check CPU's load metrics." << std::endl;
constexpr static float max_memory = 1.0;
auto max_library_size
= *std::max_element(std::begin(init_memory), std::end(init_memory)) * 1.0e-9;
rocblas_cout << "\nrocBLAS info: maximum library size per device is " << max_library_size
<< " GB." << std::endl;
if(max_library_size > max_memory)
rocblas_cerr << "\nrocBLAS info: max kernel library size " << max_library_size
<< " GB exceeds the max recommended memory " << max_memory
<< " GB. Check library logic file sizes." << std::endl;
}
size_t calculate_flush_batch_count(size_t arg_flush_batch_count,
size_t arg_flush_memory_size,
size_t cached_size)
{
size_t default_arg_flush_batch_count = 1;
size_t default_arg_flush_memory_size = 0;
size_t flush_batch_count = default_arg_flush_batch_count;
if(arg_flush_batch_count != default_arg_flush_batch_count
&& arg_flush_memory_size != default_arg_flush_memory_size)
{
rocblas_cout << "rocBLAS WARNING: cannot set both flush_batch_count and flush_memory_size"
<< std::endl;
rocblas_cout << "rocBLAS WARNING: using flush_batch_count = " << arg_flush_batch_count
<< std::endl;
flush_batch_count = arg_flush_batch_count;
}
else if(arg_flush_batch_count != default_arg_flush_batch_count)
{
flush_batch_count = arg_flush_batch_count;
rocblas_cout << "flush_memory_size = ";
print_memory_size(flush_batch_count * cached_size);
rocblas_cout << std::endl;
}
else if(arg_flush_memory_size != default_arg_flush_memory_size)
{
flush_batch_count = 1 + (arg_flush_memory_size - 1) / cached_size;
rocblas_cout << "flush_batch_count = " << flush_batch_count << std::endl;
}
return flush_batch_count;
}
//Function to limit the number of devices to be used in a mult-gpu setup
hipError_t limit_device_count(int& device_count, int max_limit)
{
hipError_t hipStatus = hipGetDeviceCount(&device_count);
if(hipStatus == hipSuccess)
device_count = std::min(device_count, max_limit);
return hipStatus;
}