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

612 lines
18 KiB
C++

/* ************************************************************************
* 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 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.
*
* ************************************************************************ */
#include "frequency_monitor.hpp"
#include "rocblas.hpp"
#include <atomic>
#include <condition_variable>
#include <future>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#ifndef _WIN32
#include <amd_smi/amdsmi.h>
#include <hip/hip_runtime.h>
template <typename T>
inline std::ostream& stream_write(std::ostream& stream, T&& val)
{
return stream << std::forward<T>(val);
}
template <typename T, typename... Ts>
inline std::ostream& stream_write(std::ostream& stream, T&& val, Ts&&... vals)
{
return stream_write(stream << std::forward<T>(val), std::forward<Ts>(vals)...);
}
template <typename... Ts>
inline std::string concatenate(Ts&&... vals)
{
std::ostringstream msg;
stream_write(msg, std::forward<Ts>(vals)...);
return msg.str();
}
#define HIP_CHECK_EXC(expr) \
do \
{ \
hipError_t e = (expr); \
if(e) \
{ \
const char* errName = hipGetErrorName(e); \
const char* errMsg = hipGetErrorString(e); \
std::ostringstream msg; \
msg << "Error " << e << "(" << errName << ") " << __FILE__ << ":" << __LINE__ << ": " \
<< std::endl \
<< #expr << std::endl \
<< errMsg << std::endl; \
throw std::runtime_error(msg.str()); \
} \
} while(0)
#define AMDSMI_CHECK_EXC(expr) \
do \
{ \
amdsmi_status_t e = (expr); \
if(e) \
{ \
const char* errName = nullptr; \
amdsmi_status_code_to_string(e, &errName); \
std::ostringstream msg; \
msg << "Error " << e << "(" << errName << ") " << __FILE__ << ":" << __LINE__ << ": " \
<< std::endl \
<< #expr << std::endl; \
throw std::runtime_error(msg.str()); \
} \
} while(0)
#endif
class FrequencyMonitorImp : public FrequencyMonitor
{
public:
const double cHzToMHz = 0.000001;
const double cMhzToHz = 1000000;
// deleting copy constructor
FrequencyMonitorImp(const FrequencyMonitorImp& obj) = delete;
#ifndef _WIN32
bool enabled()
{
static const char* env1 = getenv("ROCBLAS_BENCH_FREQ");
static const char* env2 = getenv("ROCBLAS_BENCH_FREQ_ALL");
return env1 != nullptr || (env2 != nullptr && m_isMultiXCDSupported);
}
bool detailedReport()
{
static const char* env2 = getenv("ROCBLAS_BENCH_FREQ_ALL");
return (env2 != nullptr && m_isMultiXCDSupported);
}
FrequencyMonitorImp()
{
if(!enabled())
return;
initThread();
clearValues();
}
~FrequencyMonitorImp()
{
if(!enabled())
return;
stopThread();
}
void set_device_id(int deviceId)
{
if(!enabled())
return;
m_smiDeviceIndex = GetAMDSMIIndex(deviceId);
m_XCDCount = 1;
#if AMDSMI_LIB_VERSION_MAJOR >= 25
auto status2
= amdsmi_get_gpu_xcd_counter(m_processorHandles[m_smiDeviceIndex], &m_XCDCount);
if(status2 != AMDSMI_STATUS_SUCCESS)
{
m_XCDCount = 1;
}
#endif
clearValues();
}
void start()
{
if(!enabled())
return;
clearValues();
runBetweenEvents();
}
void stop()
{
if(!enabled())
return;
assertActive();
m_stop = true;
wait();
}
double averageValueMHz(double sum, std::vector<uint64_t>& data)
{
assertNotActive();
if(enabled() && data.empty())
return 0.0;
double averageFrequency = static_cast<double>(sum / data.size());
return averageFrequency * cHzToMHz;
}
double medianValueMHz(std::vector<uint64_t>& data)
{
assertNotActive();
double median = 0.0;
if(enabled() && data.empty())
return 0.0;
size_t num_datapoints = data.size();
if(num_datapoints)
{
std::sort(data.begin(), data.end());
median = static_cast<double>(data[(num_datapoints - 1) / 2]);
if(num_datapoints % 2 == 0)
{
median = static_cast<double>(median + data[(num_datapoints - 1) / 2 + 1]) / 2.0;
}
}
return median * cHzToMHz;
}
double getLowestAverageSYSCLK()
{
std::vector<double> allAvgSYSCLK = getAllAverageSYSCLK();
double minAvgSYSCLK = allAvgSYSCLK[0];
for(int i = 1; i < m_XCDCount; i++)
{
if(allAvgSYSCLK[i] <= 0)
continue;
minAvgSYSCLK = min(minAvgSYSCLK, allAvgSYSCLK[i]);
}
return minAvgSYSCLK;
}
double getLowestMedianSYSCLK()
{
std::vector<double> allMedianSYSCLK = getAllMedianSYSCLK();
double minMedianSYSCLK = allMedianSYSCLK[0];
for(int i = 1; i < m_XCDCount; i++)
{
if(allMedianSYSCLK[i] <= 0)
continue;
minMedianSYSCLK = min(minMedianSYSCLK, allMedianSYSCLK[i]);
}
return minMedianSYSCLK;
}
std::vector<double> getAllAverageSYSCLK()
{
std::vector<double> avgSYSCLK(m_XCDCount, 0.0);
for(int i = 0; i < m_XCDCount; i++)
{
avgSYSCLK[i] = averageValueMHz(m_SYSCLK_sum[i], m_SYSCLK_array[i]);
}
return avgSYSCLK;
}
std::vector<double> getAllMedianSYSCLK()
{
std::vector<double> medianSYSCLK(m_XCDCount, 0.0);
for(int i = 0; i < m_XCDCount; i++)
{
medianSYSCLK[i] = medianValueMHz(m_SYSCLK_array[i]);
}
return medianSYSCLK;
}
double getAverageMEMCLK()
{
return averageValueMHz(m_MEMCLK_sum, m_MEMCLK_array);
}
double getMedianMEMCLK()
{
return medianValueMHz(m_MEMCLK_array);
}
double getCuCount()
{
return m_CUCount;
}
private:
void initThread()
{
m_stop = false;
m_exit = false;
InitAMDSMI();
amdsmi_version_t version;
m_isMultiXCDSupported = false;
#if AMDSMI_LIB_VERSION_MAJOR >= 25
m_isMultiXCDSupported = true;
#endif
// Initialize socket and processor handles
uint32_t socketCount = 0;
AMDSMI_CHECK_EXC(amdsmi_get_socket_handles(&socketCount, nullptr));
m_socketHandles.resize(socketCount);
AMDSMI_CHECK_EXC(amdsmi_get_socket_handles(&socketCount, m_socketHandles.data()));
for(uint32_t i = 0; i < socketCount; i++)
{
uint32_t processorCount = 0;
AMDSMI_CHECK_EXC(
amdsmi_get_processor_handles(m_socketHandles[i], &processorCount, nullptr));
size_t offset = m_processorHandles.size();
m_processorHandles.resize(offset + processorCount);
AMDSMI_CHECK_EXC(amdsmi_get_processor_handles(
m_socketHandles[i], &processorCount, &m_processorHandles[offset]));
}
m_thread = std::thread([=]() { this->runLoop(); });
return;
}
void stopThread()
{
m_stop = true;
m_exit = true;
m_cv.notify_all();
m_thread.join();
}
void runBetweenEvents()
{
assertNotActive();
{
std::unique_lock<std::mutex> lock(m_mutex);
m_task = std::move(Task([=]() { this->collect(); }));
m_future = m_task.get_future();
m_stop = false;
m_exit = false;
}
m_cv.notify_all();
}
void runLoop()
{
std::unique_lock<std::mutex> lock(m_mutex);
while(!m_exit)
{
while(!m_task.valid() && !m_exit)
{
m_cv.wait(lock);
}
if(m_exit)
{
return;
}
m_task();
m_task = std::move(Task());
}
return;
}
void collect()
{
amdsmi_frequencies_t freq;
do
{
#if AMDSMI_LIB_VERSION_MAJOR >= 25
amdsmi_gpu_metrics_t gpuMetrics;
// multi_XCD
auto status1
= amdsmi_get_gpu_metrics_info(m_processorHandles[m_smiDeviceIndex], &gpuMetrics);
if(status1 == AMDSMI_STATUS_SUCCESS)
{
for(int i = 0; i < m_XCDCount; i++)
{
m_SYSCLK_sum[i] += gpuMetrics.current_gfxclks[i] * cMhzToHz;
m_SYSCLK_array[i].push_back(gpuMetrics.current_gfxclks[i] * cMhzToHz);
}
}
#else
//XCD 0
auto status1 = amdsmi_get_clk_freq(
m_processorHandles[m_smiDeviceIndex], AMDSMI_CLK_TYPE_SYS, &freq);
if(status1 == AMDSMI_STATUS_SUCCESS)
{
m_SYSCLK_sum[0] += freq.frequency[freq.current];
m_SYSCLK_array[0].push_back(freq.frequency[freq.current]);
}
#endif
auto status2 = amdsmi_get_clk_freq(
m_processorHandles[m_smiDeviceIndex], AMDSMI_CLK_TYPE_MEM, &freq);
if(status2 == AMDSMI_STATUS_SUCCESS)
{
m_MEMCLK_sum += freq.frequency[freq.current];
m_MEMCLK_array.push_back(freq.frequency[freq.current]);
}
// collect freq every 50ms regardless of success
std::this_thread::sleep_for(std::chrono::milliseconds(50));
} while(!m_stop && !m_exit);
}
void assertActive()
{
if(!m_future.valid())
throw std::runtime_error("Monitor is not active.");
}
void assertNotActive()
{
if(m_future.valid())
throw std::runtime_error("Monitor is active.");
}
void clearValues()
{
m_SYSCLK_sum = std::vector<uint64_t>(m_XCDCount, 0);
m_SYSCLK_array = std::vector<std::vector<uint64_t>>(m_XCDCount, std::vector<uint64_t>{});
m_MEMCLK_sum = 0;
m_MEMCLK_array.clear();
}
void wait()
{
if(!m_future.valid())
return;
if(!m_stop)
throw std::runtime_error("Waiting for monitoring to stop with no end condition.");
m_future.wait();
m_future = std::move(std::future<void>());
}
void InitAMDSMI()
{
static amdsmi_status_t status = amdsmi_init(AMDSMI_INIT_AMD_GPUS);
AMDSMI_CHECK_EXC(status);
}
uint32_t GetAMDSMIIndex(int hipDeviceIndex)
{
InitAMDSMI();
hipDeviceProp_t props;
HIP_CHECK_EXC(hipGetDeviceProperties(&props, hipDeviceIndex));
m_CUCount = props.multiProcessorCount;
#if HIP_VERSION >= 50220730
int hip_version;
HIP_CHECK_EXC(hipRuntimeGetVersion(&hip_version));
if(hip_version >= 50220730)
{
HIP_CHECK_EXC(hipDeviceGetAttribute(&props.multiProcessorCount,
hipDeviceAttributePhysicalMultiProcessorCount,
hipDeviceIndex));
}
#endif
uint64_t hipPCIID = 0;
hipPCIID |= (((uint64_t)props.pciDomainID & 0xffffffff) << 32);
hipPCIID |= ((props.pciBusID & 0xff) << 8);
hipPCIID |= ((props.pciDeviceID & 0x1f) << 3);
uint32_t smiSocketCount{};
AMDSMI_CHECK_EXC(amdsmi_get_socket_handles(&smiSocketCount, nullptr));
m_socketHandles.resize(smiSocketCount);
AMDSMI_CHECK_EXC(amdsmi_get_socket_handles(&smiSocketCount, &m_socketHandles[0]));
std::ostringstream msg;
msg << "PCI IDs: [" << std::endl;
for(uint32_t device = 0; device < smiSocketCount; device++)
{
uint32_t deviceCount{};
AMDSMI_CHECK_EXC(
amdsmi_get_processor_handles(m_socketHandles[device], &deviceCount, nullptr));
m_processorHandles.resize(deviceCount);
AMDSMI_CHECK_EXC(amdsmi_get_processor_handles(
m_socketHandles[device], &deviceCount, &m_processorHandles[0]));
for(uint32_t smiIndex = 0; smiIndex < deviceCount; smiIndex++)
{
uint64_t amdSMIPCIID{};
AMDSMI_CHECK_EXC(amdsmi_get_gpu_bdf_id(m_processorHandles[smiIndex], &amdSMIPCIID));
msg << smiIndex << ": " << amdSMIPCIID << std::endl;
if(hipPCIID == amdSMIPCIID)
{
return smiIndex;
}
}
}
msg << "]" << std::endl;
throw std::runtime_error(concatenate("AMDSMI Can't find a device with PCI ID ",
hipPCIID,
"(",
props.pciDomainID,
"-",
props.pciBusID,
"-",
props.pciDeviceID,
")\n",
msg.str()));
}
using Task = std::packaged_task<void(void)>;
Task m_task;
std::atomic<bool> m_exit;
std::atomic<bool> m_stop;
std::future<void> m_future;
std::thread m_thread;
std::condition_variable m_cv;
std::mutex m_mutex;
uint32_t m_smiDeviceIndex{};
bool m_isMultiXCDSupported;
uint16_t m_XCDCount{};
uint16_t m_CUCount{};
std::vector<amdsmi_socket_handle> m_socketHandles;
std::vector<amdsmi_processor_handle> m_processorHandles;
std::vector<uint64_t> m_SYSCLK_sum;
std::vector<std::vector<uint64_t>> m_SYSCLK_array;
uint64_t m_MEMCLK_sum{};
std::vector<uint64_t> m_MEMCLK_array;
#else // WIN32
// not supporting windows for now
public:
FrequencyMonitorImp() {}
~FrequencyMonitorImp() {}
void set_device_id(int deviceId) {}
void start() {}
void stop() {}
bool enabled()
{
return false;
}
bool detailedReport()
{
return false;
}
double getLowestAverageSYSCLK()
{
return 0.0;
}
double getLowestMedianSYSCLK()
{
return 0.0;
}
std::vector<double> getAllAverageSYSCLK()
{
return std::vector<double>();
}
std::vector<double> getAllMedianSYSCLK()
{
return std::vector<double>();
}
double getAverageMEMCLK()
{
return 0.0;
}
double getMedianMEMCLK()
{
return 0.0;
}
double getCuCount()
{
return 0.0;
}
#endif
};
static FrequencyMonitorImp* g_FreqMonitorInstance{nullptr};
FrequencyMonitor& getFrequencyMonitor()
{
if(g_FreqMonitorInstance == nullptr)
{
g_FreqMonitorInstance = new FrequencyMonitorImp();
}
return *g_FreqMonitorInstance;
}
void freeFrequencyMonitor()
{
if(g_FreqMonitorInstance != nullptr)
{
delete g_FreqMonitorInstance;
g_FreqMonitorInstance = nullptr;
}
}