我想获得最大计数我必须执行一个循环才能完成x毫秒。
例如。
int GetIterationsForExecutionTime(int ms)
{
int count = 0;
/* pseudocode
do
some code here
count++;
until executionTime > ms
*/
return count;
}
我如何做到这样的事情?
答案 0 :(得分:28)
我想获得最大计数我必须执行一个循环才能完成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)我应该将垃圾收集的费用计入生产垃圾的测试中吗?如果是这样,那么请确保在测试完成之前强制完整收集。
第七,您在多线程,多处理器环境中运行代码,其中线程可以随意切换,以及线程量(操作系统将给出另一个线程的时间量,直到您的机会再次运行) )约为16毫秒。 16毫秒约为<五>百万个处理器周期。如果线程切换发生在您尝试测量的数百万个处理器周期之一内,那么提供精确的亚毫秒操作时序可能非常困难。考虑到这一点。
答案 1 :(得分:14)
var sw = new Stopwatch();
sw.Start();
...
long elapsedMilliseconds = sw.ElapsedMilliseconds;
答案 2 :(得分:6)
您还可以使用Stopwatch类:
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;
}
答案 3 :(得分:-1)
Eric Lippert的好点。 我已经进行了一段时间的基准测试和单元测试,我建议你应该丢弃每个代码导致JIT编译的第一遍。 因此,在使用循环和秒表的基准测试代码中,请记住将其置于循环的末尾:
// JIT optimization.
if (i == 0)
{
// Discard every result you've collected.
// And restart the timer.
stopwatch.Restart();
}