使用PDH获取GPU使用统计信息时遇到问题求助
PDH获取GPU利用率始终为0的排查与解决
核心问题分析
你遇到的\GPU Engine(*)\Utilization Percentage计数器返回0的问题,大概率是计数器路径不准确,或是采样逻辑不符合PDH的工作机制(GPU利用率和CPU一样,需要两次采样差值计算)。结合你的CPU功能正常的情况,重点从以下几点排查:
1. 修正计数器路径
\GPU Engine(*)\Utilization Percentage的通配符*会匹配所有GPU引擎实例(包括3D、Copy、Video Decode等),如果直接获取未被使用的引擎实例,自然返回0。要获取3D核心利用率,需要指定具体的GPU索引+引擎类型:
- 打开Windows性能监视器(
perfmon),在“添加计数器”中找到GPU Engine,展开后查看对应3D引擎的完整路径,比如:- 对于单GPU,路径可能是
\GPU Engine(0, 3D)\Utilization Percentage - 带型号的路径可能是
\GPU Engine(NVIDIA GeForce RTX 3080, 3D)\Utilization Percentage
- 对于单GPU,路径可能是
- 替换你代码中的计数器路径为上述具体路径,避免使用通配符。
2. 调整采样逻辑
PDH的利用率类计数器(包括CPU、GPU)需要两次采样并计算差值才能得到有效数值,第一次采样仅初始化基线,返回0是正常的:
- 调用
PdhCollectQueryData后,等待100~500ms(根据需求调整),再调用一次PdhCollectQueryData,之后再调用PdhGetFormattedCounterValue获取利用率。
3. 显存统计的正确计数器
如果你还需要总显存和已用显存,使用以下计数器路径(同样需要指定具体GPU实例):
- 总专用显存:
\GPU Memory(你的GPU型号)\Dedicated Capacity - 已用专用显存:
\GPU Memory(你的GPU型号)\Dedicated Usage
示例代码(C++)
以下是修正后的GPU 3D利用率获取代码,包含正确的采样逻辑和路径指定:
#include <pdh.h> #include <pdhmsg.h> #include <iostream> #include <windows.h> #pragma comment(lib, "pdh.lib") void PrintGPUStats() { PDH_HQUERY hQuery = nullptr; PDH_HCOUNTER h3DUtilCounter = nullptr; PDH_HCOUNTER hUsedMemCounter = nullptr; PDH_FMT_COUNTERVALUE counterVal; // 初始化查询 if (PdhOpenQuery(nullptr, 0, &hQuery) != ERROR_SUCCESS) { std::cerr << "PDH查询初始化失败" << std::endl; return; } // 替换为你从perfmon获取的实际计数器路径 const wchar_t* gpu3DPath = L"\\GPU Engine(0, 3D)\\Utilization Percentage"; const wchar_t* gpuUsedMemPath = L"\\GPU Memory(0)\\Dedicated Usage"; // 添加3D利用率计数器 if (PdhAddCounter(hQuery, gpu3DPath, 0, &h3DUtilCounter) != ERROR_SUCCESS) { std::cerr << "添加GPU 3D计数器失败" << std::endl; goto cleanup; } // 添加已用显存计数器 if (PdhAddCounter(hQuery, gpuUsedMemPath, 0, &hUsedMemCounter) != ERROR_SUCCESS) { std::cerr << "添加显存计数器失败" << std::endl; goto cleanup; } // 第一次采样(基线) PdhCollectQueryData(hQuery); Sleep(200); // 等待200ms获取差值 // 第二次采样 if (PdhCollectQueryData(hQuery) != ERROR_SUCCESS) { std::cerr << "采样数据失败" << std::endl; goto cleanup; } // 获取3D利用率 if (PdhGetFormattedCounterValue(h3DUtilCounter, PDH_FMT_DOUBLE, nullptr, &counterVal) == ERROR_SUCCESS) { std::cout << "GPU 3D核心利用率: " << counterVal.doubleValue << "%" << std::endl; } // 获取已用显存(单位为MB) if (PdhGetFormattedCounterValue(hUsedMemCounter, PDH_FMT_LONG, nullptr, &counterVal) == ERROR_SUCCESS) { std::cout << "已用专用显存: " << counterVal.longValue / 1024 << " MB" << std::endl; } cleanup: // 清理资源 if (h3DUtilCounter) PdhRemoveCounter(h3DUtilCounter); if (hUsedMemCounter) PdhRemoveCounter(hUsedMemCounter); if (hQuery) PdhCloseQuery(hQuery); } int main() { PrintGPUStats(); return 0; }
额外排查点
- 检查
PdhAddCounter和PdhGetFormattedCounterValue的返回值,确认没有错误代码(比如PDH_CSTATUS_NO_INSTANCE表示路径中的实例不存在)。 - 确保程序拥有足够权限(无需管理员,但如果是远程调用可能需要权限配置)。
- 如果是笔记本电脑,确认GPU未处于节能模式(部分低功耗模式下计数器可能无数据)。
内容的提问来源于stack exchange,提问作者FunkyMunky
相关产品推荐
相关产品推荐

