Skip to content

Instantly share code, notes, and snippets.

@fecf
Last active July 29, 2024 06:52
Show Gist options
  • Save fecf/2103a82afc76b5c88829c4383944a5aa to your computer and use it in GitHub Desktop.
Save fecf/2103a82afc76b5c88829c4383944a5aa to your computer and use it in GitHub Desktop.
get GPU utilization
#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;
}
@DryreL
Copy link

DryreL commented Dec 21, 2023

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

@fecf
Copy link
Author

fecf commented Dec 22, 2023

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

@yangwang201911
Copy link

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