Dot-Net

為什麼 F# inline 會導致 11 倍的性能提升

  • December 29, 2012

我正在處理一些繁重的 cpu 綁定問題。當我使用inline關鍵字時,我看到了很大的性能改進。我從標準 .net 庫中創建了一個字典,傳入了一個自定義鍵比較器,請參見下面的程式碼和計時結果

<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 上使用 inline 關鍵字

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 的數量與內聯程式碼和非內聯程式碼的巨大差異。

有人可以解釋為什麼會有如此巨大的差異嗎?

inline添加關鍵字後,我可以通過 3 倍的性能提升來重現我的機器上的行為。

在ILSpy下並排反編譯兩個版本會得到幾乎相同的 C# 程式碼。顯著的區別在於兩個相等測試:

// Version without inline
bool IEqualityComparer&lt;Program.Pair&lt;a&gt;&gt;.System-Collections-Generic-IEqualityComparer(Program.Pair&lt;a&gt; x, Program.Pair&lt;a&gt; y)
{
   a v@ = x.v@;
   a v@2 = y.v@;
   if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic&lt;a&gt;(v@, v@2))
   {
       a w@ = x.w@;
       a w@2 = y.w@;
       return LanguagePrimitives.HashCompare.GenericEqualityIntrinsic&lt;a&gt;(w@, w@2);
   }
   return false;
}

// Version with inline
bool IEqualityComparer&lt;Program.Pair&lt;int&gt;&gt;.System-Collections-Generic-IEqualityComparer(Program.Pair&lt;int&gt; x, Program.Pair&lt;int&gt; 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# 原始碼GenericEqualityIntrinsic中的函式:

let rec GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = 
   fsEqualityComparer.Equals((box x), (box y))

它對參數進行裝箱,這解釋了您的第一個範例中的大量垃圾。當 GC 過於頻繁地發揮作用時,它會顯著減慢計算速度。第二個範例(使用)在structinline時幾乎不會產生垃圾。Pair

也就是說,inline在呼叫站點使用專用版本時,這是關鍵字的預期行為。我的建議是始終嘗試在相同的基准上優化和衡量您的程式碼。

您可能對一個非常相似的執行緒感興趣為什麼這個 F# 程式碼這麼慢?.

引用自:https://stackoverflow.com/questions/14086580