我正在处理一些重的cpu绑定问题。当我使用inline
关键字时,我发现性能有了很大提升。
我从标准的.net库创建一个字典,传入一个自定义密钥Comparer,见下面的代码和时序结果
https://gist.github.com/4409734
在Eq_cmp上没有内联关键字
> perf_run 10000000 ;;
Real: 00:00:11.039, CPU: 00:00:11.029, GC gen0: 771, gen1: 3, gen2: 1
val it : unit = ()
在Eq_cmp上使用内联关键字
perf_run 10000000 ;;
Real: 00:00:01.319, CPU: 00:00:01.388, GC gen0: 1, gen1: 1, gen2: 1
val it : unit = ()
>
我还注意到Gen 0 GC与内联代码和非内联代码的数量存在巨大差异。
有人可以解释为什么会有这么大的差异吗?
答案 0 :(得分:17)
我可以在添加inline
关键字后,以3倍的性能提升重现我的计算机上的行为。
在ILSpy下并排反编译两个版本可以得到几乎相同的C#代码。明显的区别在于两个相等的测试:
// Version without inline
bool IEqualityComparer<Program.Pair<a>>.System-Collections-Generic-IEqualityComparer(Program.Pair<a> x, Program.Pair<a> y)
{
a v@ = x.v@;
a v@2 = y.v@;
if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>(v@, v@2))
{
a w@ = x.w@;
a w@2 = y.w@;
return LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>(w@, w@2);
}
return false;
}
// Version with inline
bool IEqualityComparer<Program.Pair<int>>.System-Collections-Generic-IEqualityComparer(Program.Pair<int> x, Program.Pair<int> y)
{
int v@ = x.v@;
int v@2 = y.v@;
if (v@ == v@2)
{
int w@ = x.w@;
int w@2 = y.w@;
return w@ == w@2;
}
return false;
}
通用等式的效率远低于专用版本。
我还注意到Gen 0 GC与内联代码和非内联代码的数量存在巨大差异。
有人可以解释为什么会有这么大的差异吗?
查看F# source code中的GenericEqualityIntrinsic
函数:
let rec GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool =
fsEqualityComparer.Equals((box x), (box y))
它会对参数进行装箱,这解释了第一个示例中的大量垃圾。当GC过于频繁地发挥作用时,它会大大减慢计算速度。当inline
是struct时,第二个示例(使用Pair
)几乎不产生垃圾。
也就是说,当在呼叫站点使用专用版本时,inline
关键字是预期的行为。我的建议始终是尝试在相同的基准测试中优化和测量您的代码。
您可能对非常相似的帖子Why is this F# code so slow?感兴趣。
答案 1 :(得分:15)
输入专业化
如果没有inline
,则使用效率非常低的泛型比较。使用inline
时,会删除通用性并直接使用int
比较。