查询进程性能计数器的最有效方法是什么?
What is the most performant way to query Process Performance Counters?
我正在尝试实施性能监控工具,我想监控内存和 CPU 等基本内容。
我正在尝试通过使用性能计数器来做到这一点,因为我相信这是在 C# 中查询进程性能的 "correct" 方式,这是一些示例代码:
class Program
{
static void Main(string[] args)
{
while (true)
{
var pcs = Process.GetProcesses()
.Select(p => new PerformanceCounter("Process", "Working Set - Private", p.ProcessName));
var sw = Stopwatch.StartNew();
foreach (var pc in pcs)
pc.NextValue();
Console.WriteLine($"Time taken to read {pcs.Count()} performance counters: {sw.ElapsedMilliseconds}ms");
Thread.Sleep(1000);
}
}
}
很明显,在我的系统上查询进程需要 ~12.5 毫秒,慢得令人无法接受。应该怎么做?
我已经在这个 post 中问了一个相关问题:
但我意识到我在 post 方面不够具体,问错了问题。我真正想知道的是如何使用性能计数器完成我想做的事情,还是根本不可能?
EDIT1:
我是 运行 Windows 10 Pro 1607 - Build 14393.479
Windows 有点臭名昭著的是当你在一台机器上对所有 运行ning 进程做某事时速度很慢。但这是过分的。 PerformanceCounter 的状态有些问题,我从 this question 得到了第一个提示。通过更改 Console.WriteLine() 调用,您可以在自己的测试程序中轻松地看到一些内容:
Console.Write($"Time taken to read {pcs.Count()} performance counters:");
Console.WriteLine($"{sw.ElapsedMilliseconds}ms, {GC.CollectionCount(2)} collections");
我机器上的输出:
Time taken to read 124 performance counters: 1633ms, 15 collections
Time taken to read 124 performance counters: 923ms, 30 collections
Time taken to read 124 performance counters: 928ms, 45 collections
Time taken to read 124 performance counters: 934ms, 59 collections
Time taken to read 124 performance counters: 922ms, 74 collections
Time taken to read 124 performance counters: 925ms, 89 collections
...etc
或者换句话说,每调用 ~8 次 PerformanceCounter.NextValue() 就会进行一次 完整 垃圾回收。哎哟。是的,这会使您的程序陷入困境。
委婉地说,这是非常奇怪的行为。我在 PerformanceCounter class 本身找不到任何好的线索,代码看起来很无辜。引擎盖下有一大块非托管代码很难看到(C:\Windows\Microsoft.NET\Framework\v4.0.30319\PerfCounter.dll)但是查看它的依赖项并没有提供令人信服的证据证明它有责任。这发生在 v2.0.50727 和 v4.0.30319 运行time 版本上,所以 V4.x 中的一个不幸的 hack 并不能轻易解释它。它不是 8 的一个很好的倍数,因此强制终结器为 运行 的简单计数器不容易解释它。它不是本机性能计数器,使用 WMI 查询它不会触发任何收集。它不特定于内存计数器,查询 "Thread Count" 也是如此。可能和Windows版本有关,我的是Win10 1607版(没办法再试)
Microsoft 需要参与其中。我链接的问题的作者似乎创建了一个 bug feedback report for it,我添加了这个 Q+A 作为支持证据。密切关注反馈报告,为其投票,希望他们会开始关注。如果您不想等待,也可以直接联系 Microsoft 支持。
同时,您可以使用 System.Management 命名空间。 WMI Code Creator 实用程序非常方便自动生成代码:
...
using System.Management; // Project > Add Reference required
public static void QueryWorkingset() {
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\CIMV2",
"SELECT Name, WorkingSetPrivate FROM Win32_PerfRawData_PerfProc_Process");
foreach (ManagementObject queryObj in searcher.Get()) {
Console.WriteLine("{0}: {1}", queryObj["Name"], queryObj["WorkingSetPrivate"]);
}
}
仍然没有速度恶魔,在我的机器上每个进程大约需要 1.0 毫秒。但不会像 PerformanceCounter 那样燃烧 100% 核心,也不会强制收集。
我正在尝试实施性能监控工具,我想监控内存和 CPU 等基本内容。
我正在尝试通过使用性能计数器来做到这一点,因为我相信这是在 C# 中查询进程性能的 "correct" 方式,这是一些示例代码:
class Program
{
static void Main(string[] args)
{
while (true)
{
var pcs = Process.GetProcesses()
.Select(p => new PerformanceCounter("Process", "Working Set - Private", p.ProcessName));
var sw = Stopwatch.StartNew();
foreach (var pc in pcs)
pc.NextValue();
Console.WriteLine($"Time taken to read {pcs.Count()} performance counters: {sw.ElapsedMilliseconds}ms");
Thread.Sleep(1000);
}
}
}
很明显,在我的系统上查询进程需要 ~12.5 毫秒,慢得令人无法接受。应该怎么做?
我已经在这个 post 中问了一个相关问题:
但我意识到我在 post 方面不够具体,问错了问题。我真正想知道的是如何使用性能计数器完成我想做的事情,还是根本不可能?
EDIT1:
我是 运行 Windows 10 Pro 1607 - Build 14393.479
Windows 有点臭名昭著的是当你在一台机器上对所有 运行ning 进程做某事时速度很慢。但这是过分的。 PerformanceCounter 的状态有些问题,我从 this question 得到了第一个提示。通过更改 Console.WriteLine() 调用,您可以在自己的测试程序中轻松地看到一些内容:
Console.Write($"Time taken to read {pcs.Count()} performance counters:");
Console.WriteLine($"{sw.ElapsedMilliseconds}ms, {GC.CollectionCount(2)} collections");
我机器上的输出:
Time taken to read 124 performance counters: 1633ms, 15 collections
Time taken to read 124 performance counters: 923ms, 30 collections
Time taken to read 124 performance counters: 928ms, 45 collections
Time taken to read 124 performance counters: 934ms, 59 collections
Time taken to read 124 performance counters: 922ms, 74 collections
Time taken to read 124 performance counters: 925ms, 89 collections
...etc
或者换句话说,每调用 ~8 次 PerformanceCounter.NextValue() 就会进行一次 完整 垃圾回收。哎哟。是的,这会使您的程序陷入困境。
委婉地说,这是非常奇怪的行为。我在 PerformanceCounter class 本身找不到任何好的线索,代码看起来很无辜。引擎盖下有一大块非托管代码很难看到(C:\Windows\Microsoft.NET\Framework\v4.0.30319\PerfCounter.dll)但是查看它的依赖项并没有提供令人信服的证据证明它有责任。这发生在 v2.0.50727 和 v4.0.30319 运行time 版本上,所以 V4.x 中的一个不幸的 hack 并不能轻易解释它。它不是 8 的一个很好的倍数,因此强制终结器为 运行 的简单计数器不容易解释它。它不是本机性能计数器,使用 WMI 查询它不会触发任何收集。它不特定于内存计数器,查询 "Thread Count" 也是如此。可能和Windows版本有关,我的是Win10 1607版(没办法再试)
Microsoft 需要参与其中。我链接的问题的作者似乎创建了一个 bug feedback report for it,我添加了这个 Q+A 作为支持证据。密切关注反馈报告,为其投票,希望他们会开始关注。如果您不想等待,也可以直接联系 Microsoft 支持。
同时,您可以使用 System.Management 命名空间。 WMI Code Creator 实用程序非常方便自动生成代码:
...
using System.Management; // Project > Add Reference required
public static void QueryWorkingset() {
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\CIMV2",
"SELECT Name, WorkingSetPrivate FROM Win32_PerfRawData_PerfProc_Process");
foreach (ManagementObject queryObj in searcher.Get()) {
Console.WriteLine("{0}: {1}", queryObj["Name"], queryObj["WorkingSetPrivate"]);
}
}
仍然没有速度恶魔,在我的机器上每个进程大约需要 1.0 毫秒。但不会像 PerformanceCounter 那样燃烧 100% 核心,也不会强制收集。