我当时正在使用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();
}
}
}
答案 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 的开销,但是比以前少了很多。