Last active
July 29, 2024 06:52
-
-
Save fecf/2103a82afc76b5c88829c4383944a5aa to your computer and use it in GitHub Desktop.
get GPU utilization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <chrono> | |
#include <iostream> | |
#include <regex> | |
#include <vector> | |
#include <pdh.h> | |
#include <pdhmsg.h> | |
#include <strsafe.h> | |
#include <tchar.h> | |
#pragma comment(lib, "pdh.lib") | |
// https://docs.microsoft.com/en-us/windows/win32/perfctrs/enumerating-process-objects | |
std::vector<std::pair<int, int>> GetGPURunningTimeProcess() { | |
std::vector<std::pair<int, int>> ret; | |
DWORD counterListSize = 0; | |
DWORD instanceListSize = 0; | |
DWORD dwFlags = 0; | |
const auto COUNTER_OBJECT = TEXT("GPU Engine"); | |
PDH_STATUS status = ERROR_SUCCESS; | |
status = PdhEnumObjectItems(nullptr, nullptr, COUNTER_OBJECT, nullptr, | |
&counterListSize, nullptr, &instanceListSize, | |
PERF_DETAIL_WIZARD, dwFlags); | |
if (status != PDH_MORE_DATA) { | |
throw std::runtime_error("failed PdhEnumObjectItems()"); | |
} | |
std::vector<TCHAR> counterList(counterListSize); | |
std::vector<TCHAR> instanceList(instanceListSize); | |
status = ::PdhEnumObjectItems( | |
nullptr, nullptr, COUNTER_OBJECT, counterList.data(), &counterListSize, | |
instanceList.data(), &instanceListSize, PERF_DETAIL_WIZARD, dwFlags); | |
if (status != ERROR_SUCCESS) { | |
throw std::runtime_error("failed PdhEnumObjectItems()"); | |
} | |
for (TCHAR* pTemp = instanceList.data(); *pTemp != 0; | |
pTemp += _tcslen(pTemp) + 1) { | |
if (::_tcsstr(pTemp, TEXT("engtype_3D")) == NULL) { | |
continue; | |
} | |
TCHAR buffer[1024]; | |
::StringCchCopy(buffer, 1024, TEXT("\\GPU Engine(")); | |
::StringCchCat(buffer, 1024, pTemp); | |
::StringCchCat(buffer, 1024, TEXT(")\\Running time")); | |
HQUERY hQuery = NULL; | |
status = ::PdhOpenQuery(NULL, 0, &hQuery); | |
if (status != ERROR_SUCCESS) { | |
continue; | |
} | |
HCOUNTER hCounter = NULL; | |
status = ::PdhAddCounter(hQuery, buffer, 0, &hCounter); | |
if (status != ERROR_SUCCESS) { | |
continue; | |
} | |
status = ::PdhCollectQueryData(hQuery); | |
if (status != ERROR_SUCCESS) { | |
continue; | |
} | |
status = ::PdhCollectQueryData(hQuery); | |
if (status != ERROR_SUCCESS) { | |
continue; | |
} | |
const DWORD dwFormat = PDH_FMT_LONG; | |
PDH_FMT_COUNTERVALUE ItemBuffer; | |
status = | |
::PdhGetFormattedCounterValue(hCounter, dwFormat, nullptr, &ItemBuffer); | |
if (ERROR_SUCCESS != status) { | |
continue; | |
} | |
if (ItemBuffer.longValue > 0) { | |
#ifdef _UNICODE | |
std::wregex re(TEXT("pid_(\\d+)")); | |
std::wsmatch sm; | |
std::wstring str = pTemp; | |
#else | |
std::regex re(TEXT("pid_(\\d+)")); | |
std::smatch sm; | |
std::string str = pTemp; | |
#endif | |
if (std::regex_search(str, sm, re)) { | |
int pid = std::stoi(sm[1]); | |
ret.push_back({pid, ItemBuffer.longValue}); | |
} | |
} | |
::PdhCloseQuery(hQuery); | |
} | |
return ret; | |
} | |
int64_t GetGPURunningTimeTotal() { | |
int64_t total = 0; | |
std::vector<std::pair<int, int>> list = GetGPURunningTimeProcess(); | |
for (const std::pair<int, int>& v : list) { | |
if (v.second > 0) { | |
total += v.second; | |
} | |
} | |
return total; | |
} | |
double GetGPUUsage() { | |
static std::chrono::steady_clock::time_point prev_called = | |
std::chrono::steady_clock::now(); | |
static int64_t prev_running_time = GetGPURunningTimeTotal(); | |
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); | |
std::chrono::steady_clock::duration elapsed = now - prev_called; | |
int64_t elapsed_sec = | |
std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count(); | |
int64_t running_time = GetGPURunningTimeTotal(); | |
double percentage = | |
(double)(running_time - prev_running_time) / elapsed_sec * 100; | |
// printf("percent = (%lld - %lld) / %lld * 100 = %f\n", running_time, | |
// prev_running_time, elapsed_sec, percentage); | |
prev_called = now; | |
prev_running_time = running_time; | |
if (percentage > 1.0) | |
percentage = 1.0; | |
else if (percentage < 0.0) | |
percentage = 0.0; | |
return percentage; | |
} |
Because of this gist implementation was written in header
and I guess multiple sources calling it.
There are two methods to fix
- change 3 function to inline function (e.g. inline double GetGPUUsage() { ...
- or separate this gist to .cpp/.h
hi, can we get the NPU utilization via the similar way as GPU?
I couldn't find any performance counter related to NPU via below command line in powershell.
PS C:\Users\xxx> Get-Counter -ListSet * | Where-Object {$_.Paths -like "*GPU Engine*"}
CounterSetName : GPU Engine
MachineName : .
CounterSetType : MultiInstance
Description : The running time of each gpu engine
......
Counter : {\GPU Engine(*)\Utilization Percentage, \GPU Engine(*)\Running Time}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi! Thanks for the code.
I'm getting some errors. Any idea?
10>GPUUsage.cpp.obj: Error LNK2005 : "class std::vector<struct std::pair<int,int>,class std::allocator<struct std::pair<int,int> > > __cdecl GetGPURunningTimeProcess(void)" (?GetGPURunningTimeProcess@@YA?AV?$vector@U?$pair@HH@std@@V?$allocator@U?$pair@HH@std@@@2@@std@@XZ) already defined in DryreLUIEssentialsBPLibrary.cpp.obj 10>GPUUsage.cpp.obj: Error LNK2005 : "__int64 __cdecl GetGPURunningTimeTotal(void)" (?GetGPURunningTimeTotal@@YA_JXZ) already defined in DryreLUIEssentialsBPLibrary.cpp.obj 10>GPUUsage.cpp.obj: Error LNK2005 : "double __cdecl GetGPUUsage(void)" (?GetGPUUsage@@YANXZ) already defined in DryreLUIEssentialsBPLibrary.cpp.obj