以。毫秒为单位测量.net中的代码速度

我想获得最大计数我必须执行一个循环,它需要花费x毫秒才能完成。

例如。

int GetIterationsForExecutionTime(int ms) { int count = 0; /* pseudocode do some code here count++; until executionTime > ms */ return count; } 

我如何完成这样的事情?

我想获得最大计数我必须执行一个循环,它需要花费x毫秒才能完成。

首先,根本不要这样做。 如果你需要等待一定的毫秒数, 请不要忙于等待一个循环 。 相反, 启动计时器并返回。 当计时器滴答时,让它调用一个从你离开的地方恢复的方法。 Task.Delay方法可能是一个很好用的方法; 它会为您处理计时器详细信息。

如果您的问题实际上是关于如何计算某些代码所花费的时间,那么您需要的不仅仅是一个好的计时器。 获得准确的时间安排有很多艺术和科学。

首先,你应该总是使用Stopwatch ,从不使用DateTime.Now这些时间。 秒表设计为高精度计时器,用于告诉您已经过了多长时间DateTime.Now是一个低精度计时器,用于告诉您是否该时间观看Doctor Who 。 你不会用挂钟来计时奥运会; 你可以使用最高精度的秒表,你可以得到你的手。 所以使用为您提供的那个。

其次,您需要记住C#代码是及时编译的 。 因此,由于分析循环调用的代码的抖动成本,第一次通过循环可能比后续每个时间贵几百或几千倍。 如果您打算测量循环的“温暖”成本,那么您需要开始计时之前运行循环一次。 如果您打算测量包括jit时间在内平均成本那么您需要确定合理数量的试验次数,以便平均值正常运行。

第三,你需要确保在跑步时没有佩戴任何铅块调试时切勿进行性能测量。 这样做的人数令人惊讶。 如果您在调试器中,那么运行时可能会与调试器来回交谈 ,以确保您获得所需的调试体验,并且该聊天需要时间。 抖动产生的代码比通常情况更糟 ,因此您的调试体验更加一致。 垃圾收集器的收集性较低 。 等等。 始终在调试器外部运行性能测量,并启用优化。

第四,请记住, 虚拟内存系统的成本与抖动相似 。 如果您已经在运行托管程序,或者最近运行过一个程序,那么您需要的CLR页面可能“很热” – 已经在RAM中 – 它们很快。 如果没有,那么页面可能是冷的,在磁盘上,并且需要进行页面故障。这可能会极大地改变时间。

第五,请记住抖动可以进行您不期望的优化 。 如果你试着时间:

 // Let's time addition! for (int i = 0; i < 1000000; ++i) { int j = i + 1; } 

抖动完全在其删除整个循环的权利范围内 。 它可以意识到循环不计算程序中任何其他位置使用的值,并将其完全删除,给它一个时间。 它会这样吗? 也许。 也许不吧。 这取决于抖动。 您应该测量实际代码的性能,其中计算的值实际上以某种方式使用; 然后,抖动将知道它无法优化它们。

第六,垃圾收集器可以抛弃产生大量垃圾的测试时间。 假设您有两个测试,一个产生大量垃圾,一个产生一点点。 第一次测试产生的垃圾收集的成本可以“收取”到运行第二次测试所花费的时间,如果幸运的话,第一次测试设法在没有收集的情况下运行但第二次测试触发一次。 如果您的测试产生大量垃圾,那么考虑(1)我的测试是否真实? 对不切实际的程序进行性能测量没有任何意义,因为你无法对真实程序的表现做出很好的推断。 (2)我应该将垃圾收集的费用计入产生垃圾的测试吗? 如果是这样,那么请确保在测试完成之前强制完整收集。

第七,您在multithreading,多处理器环境中运行代码,其中线程可以随意切换,并且线程量(操作系统将给予另一个线程直到您的机会再次运行的时间量)是关于的16毫秒。 16毫秒是大约五千万个处理器周期 。 如果线程切换发生在您尝试测量的数百万个处理器周期之一内,那么提供精确的亚毫秒操作时序可能非常困难。 考虑到这一点。

 var sw = new Stopwatch(); sw.Start(); ... long elapsedMilliseconds = sw.ElapsedMilliseconds; 

您也可以使用秒表类:

 int GetIterationsForExecutionTime(int ms) { int count = 0; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); do { // some code here count++; } while (stopwatch.ElapsedMilliseconds < ms); stopwatch.Stop(); return count; } 

Eric Lippert的好点。 我已经进行了一段时间的基准测试和unit testing,我建议你应该丢弃每个代码导致JIT编译的第一遍。 因此,在使用循环和秒表的基准测试代码中,请记住将其置于循环的末尾:

  // JIT optimization. if (i == 0) { // Discard every result you've collected. // And restart the timer. stopwatch.Restart(); }