【发布时间】:2019-12-08 13:12:58
【问题描述】:
最近我有机会意识到Performance Counter 的\\Process\\% Processor Time 用于单个进程,实际上与任务管理器中显示的CPU usage 不同。性能计数器通过 PerfMon API 访问,头文件 Pdh.h,而没有直接的方法来获取 TaskManager 的 CPU 使用率。
这是Microsoft TechNet 的一个帖子,讨论差异,很遗憾没有解决我的问题。
当我意识到它们提供的值之间存在巨大差异时,问题出现了。 在我的应用程序中,我需要测量 % CPU 使用率,准确或至少非常接近任务管理器提供的值。但是,PerfMon API 提供的值可能低 20-30%,为什么甚至差异是不同的并且不成比例,是另一个问题。例如,如果根据任务管理器测试进程有效负载 50-55%,PerfMon API 报告 35-40%
这是我的课程,用于使用 PerfMon API 计算总进程 CPU 使用率(已跳过返回代码检查和无关紧要的部分代码)
pdh_cpu_counter.h
class PdhCPUCounter {
public:
// @param counter_name: "\\Process(*ProcessName*)\\% Processor Time"
explicit PdhCPUCounter(const std::string& counter_name);
// Release handles here
virtual ~PdhCPUCounter();
// Provide actual CPU usage value in range [0.0, 1.0]
double getCPUUtilization() const;
private:
// Low-level query
PDH_FMT_COUNTERVALUE getFormattedCounterValue() const;
// Needed for calculation
size_t m_threads;
// Counter format: "\\Process(*ProcessName*)\\% Processor Time"
std::string m_counter_name;
// CPU counter handle
PDH_HCOUNTER m_counter = INVALID_HANDLE_VALUE;
// Query to PerfMon handle
PDH_HQUERY m_query = INVALID_HANDLE_VALUE;
};
pdh_cpu_counter.cpp
PdhCPUCounter::PdhCPUCounter(const std::string& counter_name) :
m_counter_name(counter_name),
m_threads(std::thread::hardware_concurrency())
{
PdhOpenQuery(nullptr, 0, &m_query);
PdhAddEnglishCounter(m_query, m_counter_name.c_str(), 0, &m_counter);
PdhCollectQueryData(m_query);
}
PDH_FMT_COUNTERVALUE PdhCPUCounter::getFormattedCounterValue() const
{
PdhCollectQueryData(m_query);
PDH_FMT_COUNTERVALUE val;
PdhGetFormattedCounterValue(m_counter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, nullptr, &val);
return val;
}
double PdhCPUCounter::getCPUUtilization() const
{
const auto &val = getFormattedCounterValue();
return val.doubleValue / m_threads;
}
计数器的使用:
// Monitor.exe is a test process here, which gives about 50-55% CPU usage in TaskManager
processor_time = std::make_unique<PdhCPUCounter>("\\Process(Monitor)\\% Processor Time");
// Here we're reported about 0.35 +/- 0.05
double cpu_utilization = processor_time->getCPUUtilization();
我使用 % Processor Time 计数器作为总 CPU 时间,据我所知,它应该等于 UserTime+KernelTime(根据 PerfMon 命名约定,分别为 % User Time 和 % Privileged Time)。顺便说一句,出于好奇,我检查了(UserTime+KernelTime == ProcessorTime),它不是,但这是另一个问题。
正如我从 TechNet thread 中发现的那样,Microsoft 使用 NtQuerySystemInformation 来实现任务管理器(除了官方文档之外,该来源的可信度当然低于 100%)
然而,调用
NtQuerySystemInformation(SystemProcessInformation, processInfo, processInfoSize, NULL);
填写结构的值
typedef struct _SYSTEM_PROCESS_INFO
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
}SYSTEM_PROCESS_INFO,*PSYSTEM_PROCESS_INFO;
其中也不包含 CPU 利用率的直接值!是的,它包括CreateTime、UserTime 和KernelTime,但是,目前还不清楚如何计算给定时刻的 CPU 利用率,就像 TaskManager 所做的那样
编辑总结一下,我需要一个实现,它可以让我的 CPU 使用率等于或至少足够接近 TaskManager 提供的 CPU 使用率,最好解释两个计数器来源(PerfMon 和 TaskManager)的内部结构
【问题讨论】:
-
您的实际具体问题是什么?
-
任务管理器肯定是从性能计数器获取信息
-
大卫赫弗南 不,不是。阅读 TechNet 的链接
-
问题已解决。实际上,除了SO之外,到处都得到了帮助。仍然记得它是专业人士社区的日子。 RIP StackOverflow:(
标签: c++ winapi cpu-usage performancecounter taskmanager