【问题标题】:C# Stopwatch - Returning inconsistent valuesC# 秒表 - 返回不一致的值
【发布时间】:2016-05-25 13:18:19
【问题描述】:

可能有重复,但我不知道如何解释我的问题。

我正在运行我使用 .NET 秒表实现(在不同情况下)的 Floyd Rivest 算法的性能计时。

为了更好地理解问题,这里是代码。

我正在一个接一个地执行以下3个方法:

analysis.FRStopwatch(analysis.getOrderedArray()); analysis.FRStopwatch(analysis.getDescendingOrderedArray()); analysis.FRStopwatch(analysis.getUnorderedArray());

FRStopwatch(int[]) 方法执行以下操作:

public double FRStopwatch(int[] array)
{
    frs = new FloydRivestSelection(array);
    double avg = 0;
    Stopwatch s = new Stopwatch();
    for (int i = 0; i < k.Length; i++)
    {
        Thread.Sleep(10);
        s.Start();
        frs.Select(k[i]);
        s.Stop();
        avg = avg + (double)s.Elapsed.TotalMilliseconds;
        s.Reset();
        Console.WriteLine(avg);
    }
    avg = avg / (double)k.Length;
    return avg;
}

我得到的输出如下: Original Output

可以看出,有序数组的时间比其他数组大很多。

然后我注释掉显示的第一行:

analysis.FRStopwatch(analysis.getOrderedArray());

并按以下顺序仅执行其余两个:

analysis.FRStopwatch(analysis.getDescendingOrderedArray()); analysis.FRStopwatch(analysis.getUnorderedArray());

我得到以下输出: New Output 似乎使用秒表的第一种方法工作不正常(或者其他两种工作不正常?)。我该如何解决才能使结果保持一致?问题是否与我的计算机有关(CPU 滴答声等)?

【问题讨论】:

  • 如果frs.Select(k[i]); 花费的时间少于几毫秒,这将是一种非常糟糕的计时方式。
  • @Kim 几个关键字:缓存、JIT 和可能的对象状态。远远不够完整和完美(你仍然需要使用它 cum grano salis,Eric Lippert 写了一篇关于基准测试的不错的入门级帖子)但你可能想试试这个microbenchmark utility(免责声明:我写的)
  • 最初的延迟可能是由于 JIT - 尝试在一个循环中对整个事情进行两次计时。
  • JIT 是这里的问题。

标签: c# debugging time stopwatch diagnostics


【解决方案1】:

这些行有问题:

avg = avg + (double)s.Elapsed.TotalMilliseconds;
Console.WriteLine(avg);

您正在增加平均值,但输出的是总和。试试这个来输出“当前”平均值。

avg = avg + (double)s.Elapsed.TotalMilliseconds;
Console.WriteLine(avg / (i+1));

或者,如果您对每次迭代的运行时间感兴趣:

Console.WriteLine(s.Elapsed.TotalMilliseconds);
//reset after instead of before of course
s.Reset();

【讨论】:

  • 不,它只是一个命名不佳的变量。如果您查看循环,则有一个 avg = avg / (double)k.Length; 语句,计算平均值。内部变量应该命名为sum
  • @DStanley 没错。这是循环之后。返回值正确,控制台输出不正确
【解决方案2】:

一个小请求的单个样本会受到太多变量的影响。

为了获得更好的性能指标,每次运行几千次(比如 6000 次),然后丢弃前 1000 次左右的结果以消除任何预热因素(JIT / 缓存等)。

将结果除以 5000 得到平均执行时间。这种方法将帮助您保持理智并更好地指示性能。

【讨论】:

  • 在得知这是一个 JIT 问题后,我找到了一种使用 Environment.TickCount 解决此问题的方法。
  • @KimScicluna Environment.TickCount 将如何帮助您了解 JIT 编译器?降低计时器精度?
  • 从头开始。正如您所建议的,只需在测试开始之前运行一次该方法就可以了。
【解决方案3】:

您正在向控制台打印时间的累计总和,而不是平均值或单个时间。

我建议您像这样打印各个时间:

s.Start();
frs.Select(k[i]);
s.Stop();
var elapsed = (double)s.Elapsed.TotalMilliseconds;
avg = avg + elapsed; //it is really sum, not avg
s.Reset();
Console.WriteLine(elapsed);

我猜第一种方法的第一次迭代会消耗最多的时间,这可能是因为JIT compilation。如果您打印个别时间,您会清楚地看到这一点。

另外,我建议你把变量名从avg改成sum,只计算循环后的平均值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-29
    • 2019-01-28
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    • 2019-08-03
    相关资源
    最近更新 更多