【问题标题】:Taking a computer "performance snapshot"拍摄计算机“性能快照”
【发布时间】:2014-11-20 22:36:39
【问题描述】:

我今天在工作中被要求创建一个工具,供最终用户在遇到“性能问题”时运行。这是对越来越多的关于几台计算机和/或应用程序遇到极端性能问题的报告的反应。由于报告数量众多,我们需要自动收集此数据,以便稍后深入研究数据。

我已经设置了一个后端来收集信息,并且已经在收集大量信息,但是我在收集进程的性能数据时遇到了麻烦。这是我调查的内容:

1) 使用 System.Diagnotics.Process.GetProcesses():

这为我提供了所有正在运行的 proc 的列表,这很好,但这要求应用程序能够明显提升运行,因为每当我想收集 UserProcessorTime 时它都会抛出异常,即使此信息可用于每个非-admin 用户在任务管理器中用于他自己的进程。即使以成熟的管理员用户身份运行,它也会引发异常。

2) 使用性能计数器

性能计数器似乎非常强大,但我在按进程 ID 匹配性能计数器时遇到了麻烦,而且性能计数器似乎主要用于跟踪一段时间内的性能。这不是这里的情况。我们主要是在寻找一种流程失控的模式。

基本上,我们正在寻找的(或多或少)是任务管理器的进程列表中显示的信息的快速快照(进程名称、cpu 周期、私有内存使用情况),而无需应用程序运行提升(进程列表可以限制为用户进程)。这符合我的要求。

编辑:这是我尝试使用的一些代码,抱歉之前没有提供任何代码

这是一个尝试使用性能计数器来获取所有进程的“% Processor Time”和“Working Set - Private”的示例。

PerformanceCounterCategory perfCategory = PerformanceCounterCategory.GetCategories().First(c => c.CategoryName == "Process");
Console.WriteLine("{0} [{1}]", perfCategory.CategoryName, perfCategory.CategoryType);
string[] instanceNames = perfCategory.GetInstanceNames();

if (instanceNames.Length > 0)
{
    foreach (string instanceName in instanceNames)
    {
        Console.WriteLine("    {0}", instanceName);
        PerformanceCounter[] counters = perfCategory.GetCounters(instanceName);

        foreach (PerformanceCounter counter in counters)
        {
            if (counter.CounterName == "Working Set - Private")
            {
                Console.WriteLine("        {0} - {1}", counter.CounterName, counter.RawValue, counter.RawValue / 1024);
            }
            if (counter.CounterName == "% Processor Time")
            {
                Console.WriteLine("        {0} - {1}", counter.CounterName, counter.RawValue);
            }
        }
    }
}

“Working Set - Private”的结果是正确的,但对于“% Processor Time”,这些值毫无意义。

这是一个输出示例,如您所见,% Processor Time 结果毫无意义:

WDExpress#1
    % Processor Time - 378146424
    Working Set - Private - 136257536
taskhost
    % Processor Time - 129480830
    Working Set - Private - 1835008
svchost
    % Processor Time - 2877438445
    Working Set - Private - 4096000
IpOverUsbSvc
    % Processor Time - 15600100
    Working Set - Private - 1236992
vncserver
    % Processor Time - 238213527
    Working Set - Private - 2555904
chrome#21
    % Processor Time - 1652830595
    Working Set - Private - 56799232

编辑 2:

Scott 已经提供了大部分答案,但现在我仍然面临以下两个选项的其他问题:

  1. 快速,不准确,轰炸 CPU

    counter.NextValue();
    Console.WriteLine("{0} - {1}", counter.CounterName, counter.NextValue());
    

在彼此之后调用 NextValue() 两次可以非常快速地收集数据,但几乎会轰炸我的 CPU(并且结果可能不是最佳的)。

  1. 准确,非常慢,在 CPU 上很容易

    counter.NextValue();
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine("{0} - {1}", counter.CounterName, counter.NextValue());
    

另一种选择是在每次调用 NextValue() 之间使用 Thread.Sleep(),但这会使其非常慢(因为我们可能正在谈论 100 多个进程)

【问题讨论】:

  • 你的问题到底是什么?您尝试过的一些代码 sn-ps 怎么样?
  • “我怎样才能快速快照显示在任务管理器的进程列表中的信息(进程名称、cpu 周期、私有内存使用情况),而无需运行提升的应用程序”基本上。我没有提供任何 sn-ps,因为我在编写代码时没有任何问题,我只是不确定要使用哪些组件。我也是在临睡前写的:)
  • 使用 WMI 访问进程信息怎么样?比如:codeproject.com/Articles/12138/…
  • 您想收集哪些信息?我看到的唯一具体提到的是UserProcessorTime。在我们告诉您如何收集之前,我们需要知道您想要收集什么。此外,通常“性能快照”确实使用性能计数器,并在几秒到几分钟之间为您提供信息的运行平均值。单个时刻快照没有那么有用。 (例如,如果某个进程敲击了 HDD 的 IO,但每分钟只执行一次,持续 20 秒。如果您的即时快照不在该窗口中,您将看不到它)
  • @techvice 我在几个地方读到,使用 WMI 会非常慢(不能引用任何消息来源,我很抱歉)。但我会调查一下。

标签: c# .net performance performancecounter


【解决方案1】:

你的处理器时间问题是你不应该在它上面使用RawValue(你真的不应该将它用于工作集)。对于处理器时间,您必须调用NextValue 进行两个样本,一个是您获得第二个样本,您将获得准确的数字。

PerformanceCounterCategory perfCategory = PerformanceCounterCategory.GetCategories().First(c => c.CategoryName == "Process");
Console.WriteLine("{0} [{1}]", perfCategory.CategoryName, perfCategory.CategoryType);
string[] instanceNames = perfCategory.GetInstanceNames();

if (instanceNames.Length > 0)
{
    foreach (string instanceName in instanceNames)
    {
        Console.WriteLine("    {0}", instanceName);
        PerformanceCounter[] counters = perfCategory.GetCounters(instanceName);

        //Go through each processor counter once to get the first value, should just return 0.
        foreach (PerformanceCounter counter in counters)
        {
            if (counter.CounterName == "% Processor Time")
            {
                counter.NextValue();
            }            
        }

        //You need to wait at least 100ms between samples to get useful values, the longer you wait the better the reading.
        Thread.Sleep(150);

        foreach (PerformanceCounter counter in counters)
        {
            if (counter.CounterName == "Working Set - Private")
            {
                Console.WriteLine("        {0} - {1}", counter.CounterName, counter.RawValue, counter.RawValue / 1024);
            }
            if (counter.CounterName == "% Processor Time")
            {
                Console.WriteLine("        {0} - {1:N1}", counter.CounterName, counter.NextValue());
            }
        }
    }
}

【讨论】:

  • 谢谢!做到了。我之前尝试过使用 NextValue(),但我认为它会在初始化和第一次调用 NextValue() 之间进行测量,所以我只使用了一次。不过还是有些麻烦。在彼此之后调用 NextValue() 两次可以非常快速地收集数据,但几乎会轰炸我的 CPU(并且结果可能不是最佳的)。另一种选择是在每次调用 NextValue() 之间使用 Thread.Sleep(),但这会使其非常慢(因为我们可能正在谈论 100 多个进程)
  • 对于您在问题中编辑的“快速不准确,炸弹 CPU”解决方案,您忽略了我在示例中的评论 You need to wait at least 100ms between samples to get useful values, the longer you wait the better the reading. 如果您不等待至少 100 毫秒,您只会得到 0或 NextValue 的结果中的 100,有关详细信息,请参阅 this old answer of mine。不要每个进程等待一次,读取所有进程一次,等待100毫秒,然后再次读取所有进程。
  • 好答案,我从没想过只为所有计数器执行一次 Thread.Sleep,这实际上应该始终如此。不像网络中的所有其他示例,睡眠是按计数器完成的(我猜是因为问题总是单计数器)
猜你喜欢
  • 1970-01-01
  • 2014-05-23
  • 2013-02-14
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-03
  • 1970-01-01
相关资源
最近更新 更多