我试图实现一个性能监视工具,我想监视内存和CPU等基本内容。
我试图通过使用性能计数器这样做,因为我相信这是"正确的"在C#中查询进程性能的方法,这是一些示例代码:
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毫秒是不可接受的慢。它应该如何完成?
我已经在这篇文章中提到了一个相关的问题:Performance Counter read access very slow - How does Task Manager do it?
但是我意识到我在那篇文章中不够具体,并且问了错误的问题。我真的想知道我怎么能用性能计数器做我想做的事情,或者根本不可能做到这一点?
EDIT1 :
我正在运行Windows 10 Pro 1607 - Build 14393.479
答案 0 :(得分:11)
当你对机器上所有正在运行的进程执行某些操作时,Windows有点臭名昭着。但这太过分了。在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
或换句话说,对于每次调用PerformanceCounter.NextValue()的~8次,完整的垃圾收集。哎哟。是的,这会让你的计划陷入困境。
这是非常奇怪的行为,说得客气一点。我在PerformanceCounter类本身找不到任何好的提示,代码看起来非常无辜。很难看到很多非托管代码(C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ PerfCounter.dll),但查看其依赖关系并不能提供令人信服的证据证明它是负责任的。在v2.0.50727和v4.0.30319运行时版本上都会发生这种情况,因此V4.x中的一个不幸的黑客攻击并不容易解释它。它不是8的很好的倍数所以一个简单的计数器迫使终结者运行并不容易解释它。它不是本机性能计数器,使用WMI查询它不会触发任何集合。它并不特定于内存计数器,例如,查询"线程计数"也做到了。可能与Windows版本有关,我的是Win10版本1607(不能轻易测试另一个版本)。
微软需要参与其中。我链接的问题的作者似乎创建了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%的核心,也没有强制收集。