【问题标题】:What is the most performant way to query Process Performance Counters?查询进程性能计数器的最高效方法是什么?
【发布时间】:2017-04-21 18:01:05
【问题描述】:

我正在尝试实现一个性能监控工具,我想监控基本的东西,例如内存和 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 专业版 1607 - 内部版本 14393.479

【问题讨论】:

  • 您可以随时use the raw Win32 API.. 或并行运行一些任务。
  • 顺便说一句,Memory Performance MSDN page“内存性能信息可通过系统性能计数器和 GetPerformanceInfo、GetProcessMemoryInfo 和 GlobalMemoryStatusEx 等函数从内存管理器获得。应用程序如 Windows任务管理器、可靠性和性能监视器以及 Process Explorer 工具使用性能计数器来显示系统和各个进程的内存信息。”
  • @stuartd 你知道吗,我完全忘记了线程。我确实可以并行运行它们!我想知道这会对性能产生什么影响..
  • @stuartd 并行执行它仍然太慢:(

标签: c# windows performance performancecounter query-performance


【解决方案1】:

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

或者换句话说,每约 8 次 PerformanceCounter.NextValue() 调用进行一次完整 垃圾收集。哎哟。是的,这会让你的程序陷入困境。

委婉地说,这是非常奇怪的行为。我在 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% 核心,也不会强制收集。

【讨论】:

  • 谢谢您,先生,这非常有帮助。我将使用我的 Windows 版本 (10) 更新我的原始帖子,并将我的声音添加到该错误报告中,并通过我的一个渠道联系 Microsoft。在我们有官方解决方案之前,我已将此答案标记为正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2015-08-06
  • 1970-01-01
相关资源
最近更新 更多