为什么 Func<> 委托这么慢
Why are Func<> delegates so much slower
我正在使用 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();
}
}
}
以下是基准:
您在每个 调用时创建一个新的委托对象。这有相当大的开销也就不足为奇了。
如果您使用不捕获 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 |
所以仍然有 位 的开销,但比以前少了很多。
我正在使用 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();
}
}
}
以下是基准:
您在每个 调用时创建一个新的委托对象。这有相当大的开销也就不足为奇了。
如果您使用不捕获 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 |
所以仍然有 位 的开销,但比以前少了很多。