我的静态方法似乎通过重用执行得更快。为什么?它会被缓存吗?

My static method seems to perform faster with reuse. Why? Does it become cached?

我创建了代码来查看我的静态压缩方法的执行速度,我注意到第一次执行需要 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();
    }
}

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 编译是对您注意到的内容的解释。

当你第一次击中它时,它会感到紧张。所以这需要时间。不确定为什么第二次和第三次不同。