我的静态方法似乎在重用时表现得更快。为什么?它会被缓存吗?

时间:2016-12-29 05:51:39

标签: c# performance time compression benchmarking

我创建了代码来查看我的静态压缩方法执行的速度有多快,我注意到第一次执行需要8,500,000纳秒,第二次执行需要大约一半,然后在0纳秒之后执行。为什么呢?

private void CheckPerformance()
{
    while (KeepRunning)
    {
        //generates a complex random 500 character string
        string text = GenerateString(500, 4);


        DateTime beginTime = DateTime.Now;

        byte[] bytes = Compress(text); // < - timing this

        long elapsedTicks = DateTime.Now.Ticks - beginTime.Ticks;
        Console.WriteLine("   {0:N0} nanoseconds", elapsedTicks * 100);

        //sleep for 5 seconds
        Thread.Sleep(5000);
    }
}

public static byte[] Compress(string text)
{
    using (MemoryStream output = new MemoryStream())
    {
        using (DeflateStream ds = new DeflateStream(output, CompressionMode.Compress))
        {
            using (StreamWriter writer = new StreamWriter(ds, Encoding.UTF8))
            {
                writer.Write(text);
            }
        }
        return output.ToArray();
    }
}

2 个答案:

答案 0 :(得分:1)

DateTime.Now每秒更新大约10次,但不要引用我(可能取决于硬件和软件设置)。它也很慢,因为它需要弄清楚系统的时区。 UtcNow速度更快,但仍会缓存一段时间。因此,它可能在后续调用中使用缓存版本。

使用StopWatch代替更准确的测量。 StopWatch通过利用硬件实现高精度。您可以使用Stopwatch.IsHighResolution进行检查。

using System.Diagnostics;

Stopwatch sw = new Stopwatch();

sw.Start();

// code to benchmark 

sw.Stop();

让我们看看你是否得到相同的指标。

修改

虽然您的方法确实需要进行JIT编译,但差异不能归因于JIT编译,因为它只会被JIT编译一次(并不总是,但在您的情况下它将是一次),然后重用。因此,只有第一次呼叫需要更长时间,后续呼叫应该相同。要抛弃这个假设,只需在基准测试阶段之外调用Compress,以便进行JIT编译。然后对它进行基准测试,现在不会发生JIT编译,DateTime仍然会给你随机结果,因为它是缓存的。

注意:JIT编译器不一定总是将整个方法编译成机器代码,但仅在执行通过代码时才编译。因此,如果您有if语句,则在通过块传递执行之前,可能无法编译块。但是你的if语句没有,所以这就是JIT编译一次的原因。

此外,我们无法自信地说这是由于JIT编译,因为Compress方法可能是inlined,但在您的情况下很可能没有内联,因为您很可能已经调试器打开,因此,JIT优化将被禁用。

尝试使用此代码,即使执行相同的代码,您也会注意到它会为经过的时间提供随机结果:

for (int i = 0; i < 1000; i++)
{
    DateTime beginTime = DateTime.UtcNow;

    var sw = Stopwatch.StartNew();

    while (sw.ElapsedTicks < 100)
    {
        Console.WriteLine("*");
    }
    long elapsedTicks = DateTime.UtcNow.Ticks - beginTime.Ticks;
    Console.WriteLine("   {0:N0} nanoseconds", elapsedTicks * 100);
}

在我的系统上,如果我将此行更改为sw.ElapsedTicks < 2050,则始终存在一致的非零报告差异。这意味着当DateTime.Now获得新值而不是使用缓存值时。

总之,我不认为JIT编译是你注意到的解释。

答案 1 :(得分:1)

当你第一次碰到它时,它会被咬住。所以这需要时间。不确定为什么第二次和第三次不同。