为什么F#inline会导致11倍的性能提升

时间:2012-12-29 22:45:21

标签: .net performance struct f# inline

我正在处理一些重的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与内联代码和非内联代码的数量存在巨大差异。

有人可以解释为什么会有这么大的差异吗?

2 个答案:

答案 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比较。