1
8
我的第一个想法是一个简单的循环
相比之下有点傻:
原因是(1)这种“for”循环的开销非常小,以至于即使test()只需要一微秒,也几乎不值得担心,(2)timer.Start()和timer.Stop()都有自己的开销,这可能比for循环更影响结果。也就是说,我在Reflector中浏览了一下秒表,发现Start()和Stop()相当便宜(考虑到所涉及的数学问题,调用Elapsed*属性可能更贵) 确保秒表的IsHighResolution属性为true。如果为false,则秒表使用DateTime.UtcNow,我认为它仅每15-16毫秒更新一次。 一。获得每个迭代的运行时间通常是件好事吗? 通常不需要测量每个单独迭代的运行时,但是 是 有助于了解不同迭代之间的性能变化。为此,可以计算最小值/最大值(或k个异常值)和标准偏差。只有“中值”统计需要记录每次迭代。 如果您发现标准差很大,那么您可能有理由记录每个迭代,以便探索时间为什么不断变化。 有些人编写了一些小框架来帮助您进行性能基准测试。例如, CodeTimers . 如果您正在测试的对象非常微小和简单,以至于基准库的开销很重要,请考虑在基准库调用的lambda中的for循环中运行该操作。如果操作太小以至于for循环的开销很重要(例如,测量乘法的速度),那么使用手动循环展开。但是,如果使用循环展开,请记住,大多数实际应用程序不使用手动循环展开,因此您的基准测试结果可能会夸大实际性能。
2。在实际计时开始前有一个小循环运行是否也很好? 您测量哪些迭代取决于您最关心的是启动时间、稳态时间还是总运行时间。一般来说,将一次或多次运行单独记录为“启动”运行可能很有用。您可以期望第一次迭代(有时不止一次)运行得更慢。作为一个极端的例子,我的 GoInterfaces 库始终需要140毫秒来产生第一个输出,然后在大约15毫秒内再输出9个。 根据基准测试的内容,您可能会发现,如果在重新启动后立即运行基准测试,则第一次迭代(或前几次迭代)将运行得非常缓慢。然后,如果您再次运行基准测试,第一次迭代将更快。 三。循环中的强制Thread.Yield()是否有助于或损害CPU绑定测试用例的计时?
|
2
4
不管函数计时的机制是什么(这里的答案似乎很好),有一个非常简单的技巧可以消除基准代码本身的开销,即循环、计时器读数和方法调用的开销:
这将为您提供一个计时开销的基线,您可以从实际基准函数的后一个度量中减去该基线。
当然,您必须重新安排一点基准代码。理想情况下,您需要使用 为了对空函数和实际的基准函数进行基准测试,我建议您将计时循环移到另一个函数中,或者至少保持这两个循环 完全地 一模一样。总结
通过这样做,实际的计时机制突然变得不那么重要了。 |
3
2
我认为您的第一个代码示例似乎是最好的方法。 您的第一个代码示例是小的、干净的和简单的,并且在测试循环期间不使用任何主要的抽象,这可能会带来隐藏的开销。 使用Stopwatch类是一件好事,因为它简化了通常需要编写以获得高分辨率计时的代码。 您可以考虑的一件事是提供一个选项,在进入计时循环以预热测试例程可能执行的任何缓存、缓冲区、连接、句柄、套接字、线程池线程等之前,对测试进行较少次数的迭代。 哦。 |
4
1
我倾向于同意@ Sam Saffron 关于每次迭代使用一个秒表而不是一个秒表。在您的示例中,默认情况下执行1000000次迭代。我不知道制作一块秒表的成本是多少,但你要制作一百万块。可以想象,这本身可能会影响你的测试结果。我对您的“最终实现”进行了一些修改,允许在不创建1000000个秒表的情况下测量每个迭代。当然,由于我正在保存每次迭代的结果,所以我正在分配1000000个long,但是乍一看,这似乎比分配那么多秒表的总体影响要小。我还没有将我的版本与你的版本进行比较,看我的版本是否会产生不同的结果。
使用时间戳和频率来计算性能并不像直接使用秒表实例那么简单。但是,对每个迭代使用不同的秒表可能不如使用单个秒表来测量整个过程那么清楚。
我也同意热身循环。取决于你的测试正在做什么,可能有一些固定的启动成本,你不想影响整体结果。启动循环应该会消除这个问题。 由于保存整个值数组(或计时器)所需的存储成本,保持每个单独的计时结果会适得其反。对于更少的内存,但更多的处理时间,您可以简单地求和增量,计算最小值和最大值。这有可能会丢掉你的结果,但是如果你主要关心的是基于独立迭代测量生成的统计数据,那么你可以在时间增量检查之外进行最小和最大计算:
看起来很老的学校没有林肯的手术,但它仍然能完成任务。 |
5
0
方法2中的逻辑对我来说更正确,但我只是一个CS学生。 我发现了一个你可能会感兴趣的链接: http://www.yoda.arachsys.com/csharp/benchmark.html |
6
0
|
7
0
我有一个类似的 question here . 我更喜欢使用一个秒表的概念,特别是如果你是微型工作台。您的代码没有考虑可能影响性能的GC。 我认为在运行测试运行之前强制GC集合是非常重要的,而且我不确定100次预热运行的意义是什么。 |
8
0
不过,要考虑的一件事是,CPU缓存未命中的影响是否真的是一件公平的事情?
一个基于数组或单链表的队列就是一个例子;当缓存线在两个调用之间没有被重新填充时,前者几乎总是有更高的性能,但是在调整大小操作上比后者更困难。因此,后者可以在现实世界中的情况下获胜(因为它们更容易以无锁的形式编写),即使它们几乎总是在快速迭代的计时测试中失败。 出于这个原因,还可以尝试一些迭代来强制刷新缓存。我不知道现在最好的办法是什么,所以如果我这样做的话,我可能会回来补充。 |
Robert King · Unity C#语法问题-转换位置 1 年前 |
JBryanB · 如何从基本抽象类访问类属性 1 年前 |
law · 检查答案按钮的输入字符串格式不正确 2 年前 |
i_sniff_ket · 在unity之外使用unity类 2 年前 |