性能计数器NextValue()很慢(1,000+个计数器)

8
在我们的应用程序中,我们使用Windows性能计数器来存储一些应用程序指标,稍后在某些Web服务中检索这些指标。
我遇到了从计数器读取值所需的时间过长的问题。我已经查看了我的应用程序的其余部分,性能方面都很好,但是在循环中(从List或array)读取计数器需要很长时间。
示例代码:
// This triggers a read of the counter's initial value (1000ms delay following for calculated counters)
counters.ToList().ForEach(counter => counter.NextValue());

在我测试的循环中,1,359个计数器需要20秒,经过秒表测量,读取计数器值的平均时间为0-10ms或80-90ms左右。其中许多计数器需要0ms,最高的大约为170ms,非零平均值约为80-90ms。也许我太乐观了,但我本来以为读取1,000个数字值只需要几毫秒。这里是否有更多的处理比我所知道的要多?实际上,在我的逻辑后面还有另一个循环,获取计算计数器的第二个值。这使得情况变得更糟。:)谢谢!

更新1

我在计数器检索中添加了一个秒表,并对结果感到惊讶。即使是读取.RawValue这样简单的属性,仍然需要很长时间。据我所知,所有计数器基本上都是相同的,检索应该非常快;奇怪的是,我还看到一种模式,即网络类别的计数器需要更长时间。

根据http://joe.blog.freemansoft.com/2014/03/windows-performance-counters.html,性能计数器服务的性能甚至不应该考虑。

我已经将一些秒表结果发布到以下pastebin:http://pastebin.com/raw.php?i=aDJk2Tru

我的代码如下:

Stopwatch t;
foreach (var c in counters)
{
    t = Stopwatch.StartNew();
    var r = c.RawValue;
    Debug.WriteLine(t.ElapsedMilliseconds.ToString("000") + " - " + c.CategoryName + ":" + c.CounterName + "(" + c.CounterType + ") = " + r);
}

正如您在粘贴中看到的那样,很多读数为0,但是有很多处于50-100毫秒的范围内。我真的不明白这是怎么回事。毕竟,一个计数器值应该与任何其他值一样快,对吧?

@keyboardP 这与 LINQ 生成的代码有很大不同吗? - FreeAsInBeer
@FreeAsInBeer - 不,它不应该是这样的,但我只是想了解问题的基本组成部分。 - keyboardP
@dbc 这是从本地计算机(实际上是我的个人计算机,因为我正在开发)读取的。我使用PerformanceCounterCategory.GetCounters()生成列表,并从perfmon中选择了一堆随机类别,这给了我一个1,359个计数器的列表。我不确定为什么NextValue()函数不是微不足道的;我是否天真地认为性能服务只是一个数字存储库? :) - trnelson
1
也许你可以在foreach循环中使用stopwatch,并记录每个计数器的时间,然后将这些信息提供给我们。也许知道谁是执行时间为170ms的人,谁是执行时间为0ms的人,可以让我们更好地了解花费时间长的原因。 - Scott Chamberlain
@ScottChamberlain 我已经更新了问题并提供了更多信息。我仍然困惑为什么有些计数器比其他计数器需要更长的时间。除了可能是类别之外,似乎没有模式,但这真的重要吗?资源写入计数器时是否可能会对它们进行锁定? - trnelson
显示剩余3条评论
1个回答

10

以下是我关于计数器的调查结果。请原谅语法错误;这是从我发送的一封电子邮件中提取出来的。

  • 在我的机器上,从计数器类别中读取实例名称需要4-5秒的处理时间(在服务器上可能更好或更差,不确定)。这个时间与类别中计数器的数量变化微不足道。如果您不使用实例计数器,可以避免这种情况。
  • 我们将所有计数器存储在单个类别中,因此,考虑到我们的情况,该类别最终将不可避免地拥有数千个计数器。在我的测试中,类别中的计数器越多,性能越差。这似乎是有道理的,但是一个单独计数器的性能受到当前内存中计数器的数量的影响,这是一种奇怪的相关性,也许:
    • 有8个计数器时,每个计数器的读取时间约为1-2毫秒
    • 有256个计数器时,每个计数器的读取时间约为15-18毫秒
    • 有512个计数器时,每个计数器的读取时间约为30毫秒
    • 有3,584个计数器时(读取所有计数器),每个计数器的读取时间约为200毫秒
    • 系统中有3,584个计数器(在内存中过滤后,仅读取512个计数器),每个计数器的读取时间在50-90毫秒之间。不确定为什么这些比前面的512个计数器慢。
    • 我使用System.Diagnostics.Stopwatch运行了每个测试几次以计时。
  • 需要注意的重要事项是,计数器必须读取两次,因为许多计数器是在一段时间内计算出来的,并呈现出开始和结束读取时间之间的平均值,因此这些糟糕的数字在实际情况下会更糟。
根据上述数字,在我的机器上,每个计数器大约需要50毫秒的时间,加上实例查询和第二个计数器读取,我们需要约60秒才能完成一个请求。这是在一次只处理512个计数器的情况下。我已经多次在我的机器上对服务运行了完整的查询,请求始终在60-65秒内完成。
我肯定不会认为单个计数器的性能会因为其他计数器的数量而受到影响。通过我的阅读,Windows Performance Monitor系统应该很快,而且在小集合中确实如此。我们可能滥用了系统,所以导致性能下降。
更新
考虑到我们可以控制如何创建计数器,我们决定稍微改变一下方法。我们不再使用少量分类但是有很多计数器的方式,而是创建许多分类,每个分类有较少的计数器(每个分类4-8个计数器)。这种方法使我们有效地避免了性能问题,并且计数器读取时间在0-1毫秒范围内。在我们目前的经验中,即使有100个每个分类只有几个计数器的新分类也不会对系统性能产生任何影响。
重要提示:在处理大量的附加计数器时,您需要考虑性能计数器默认设置的内存限制。这可以通过machine.config或注册表项来完成。更多信息请参见:http://msdn.microsoft.com/en-us/library/ms229387(v=vs.110).aspx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接