10

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

例如。

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

    return count;
}

我该如何完成这样的事情?

4

4 回答 4

30

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

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

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

首先,您应该始终使用Stopwatch并且永远不要使用DateTime.Now这些时间。秒表被设计成一个高精度计时器,用于告诉您经过了多少时间DateTime.Now是一个低精度计时器,用于告诉您是否该看神秘博士了。你不会用挂钟来计时奥运会比赛;你会使用你能拿到的最高精度的秒表。因此,请使用为您提供的那个。

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

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

第四,请记住,虚拟内存系统会产生类似于抖动的成本。如果您已经在运行托管程序,或者最近运行了一个托管程序,那么您需要的 CLR 页面可能是“热的”——已经在 RAM 中——它们速度很快。如果不是,那么这些页面可能是冷的,在磁盘上,需要进行页面错误。这会极大地改变时间。

第五,请记住,抖动会产生您意想不到的优化。如果您尝试计时:

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

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

第六,产生大量垃圾的测试时间可能会被垃圾收集器丢弃。假设你有两个测试,一个会产生很多垃圾,一个会产生一点点垃圾。如果幸运地第一个测试设法在没有收集的情况下运行但第二个测试触发了一个,则第一个测试产生的垃圾收集的成本可以“收取”到运行第二个测试所花费的时间。如果您的测试产生大量垃圾,那么请考虑(1)我的测试一开始是否现实?对不切实际的程序进行性能测量没有任何意义,因为您无法很好地推断出真实程序的行为方式。并且(2)我应该向产生垃圾的测试收取垃圾收集的成本吗?如果是这样的话,

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

于 2012-04-11T18:36:31.867 回答
15
var sw = Stopwatch.StartNew();
...
long  elapsedMilliseconds = sw.ElapsedMilliseconds;
于 2012-04-11T17:49:11.680 回答
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;
}
于 2012-04-11T17:48:21.663 回答
-1

Eric Lippert 的优点。我已经进行了一段时间的基准测试和单元测试,我建议你应该放弃代码的每一次首次传递,因为 JIT 编译。因此,在使用循环和秒表的基准测试代码中,请记住将其放在循环的末尾:

                // JIT optimization.
                if (i == 0)
                {
                    // Discard every result you've collected.
                    // And restart the timer.
                    stopwatch.Restart();
                }
于 2017-01-09T00:06:10.220 回答