为什么Func <>代表要慢得多

时间:2020-08-03 13:05:58

标签: c# .net performance .net-core benchmarking

我当时正在使用funcs将重复的算术代码移动到可重用的块中,但是当我运行一个简单的基准测试是否会更慢时,我感到惊讶的是它慢了两倍。

为什么对表达式的求值速度要慢两倍?

using System;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Calculations>();

            var logger = ConsoleLogger.Default;
            MarkdownExporter.Console.ExportToLog(summary, logger);

            Console.WriteLine(summary);
        }
    }

    public class Calculations
    {
        public Random RandomGeneration = new Random();

        [Benchmark]
        public void CalculateNormal()
        {
           var s =  RandomGeneration.Next() * RandomGeneration.Next();
        }

        [Benchmark]
        public void CalculateUsingFunc()
        {
            Calculate(() => RandomGeneration.Next() * RandomGeneration.Next());
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int Calculate(Func<int> expr)
        {
            return expr();
        }
    }
}

以下是基准: enter image description here

1 个答案:

答案 0 :(得分:14)

您要在每次调用中创建一个新的委托对象。产生大量开销也就不足为奇了。

如果您使用无法捕获this的lambda表达式或任何局部变量(在这种情况下,编译器可以将其缓存在静态字段中),或者您明确创建了一个实例并将其存储在自己一个领域,大部分开销都消失了。

这是您测试的修改版本:

public class Calculations
{
    public Random RandomGeneration = new Random();
    private Func<int> exprField;
    
    public Calculations()
    {
        exprField = () => RandomGeneration.Next() * RandomGeneration.Next();
    }
    
    [Benchmark]
    public void CalculateNormal()
    {
       var s =  RandomGeneration.Next() * RandomGeneration.Next();
    }

    [Benchmark]
    public void CalculateUsingFunc()
    {
        Calculate(() => RandomGeneration.Next() * RandomGeneration.Next());
    }
    
    [Benchmark]
    public void CalculateUsingFuncField()
    {
        Calculate(exprField);
    }

    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int Calculate(Func<int> expr)
    {
        return expr();
    }
}

结果在我的机器上

|                  Method |     Mean |    Error |   StdDev |
|------------------------ |---------:|---------:|---------:|
|         CalculateNormal | 27.61 ns | 0.438 ns | 0.388 ns |
|      CalculateUsingFunc | 48.74 ns | 1.009 ns | 0.894 ns |
| CalculateUsingFuncField | 32.53 ns | 0.698 ns | 0.717 ns |

所以仍然有 bit 的开销,但是比以前少了很多。