2012-01-12 74 views
2

我在一個項目上工作時意外地注意到,只有一個額外的(未使用的)參數的同一個方法的管理運行速度比另一個快10倍,並啓用了優化。幾乎相同的方法之間的極好的性能差異

type Stream() = 
    static member private write (x, o, a : byte[]) = (for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256)); 4 
    static member private format f x l = Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a) 
    static member private format1 f x l o = Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a) 
    static member Format (value : int) = Stream.format (fun (x: int, i, a) -> Stream.write(x, i, a)) value 4 
    static member Format1 (value : int) = Stream.format1 (fun (x: int, i, a) -> Stream.write(x, i, a)) value 4 

測試時,Stream.Format1運行比Stream.Format快得多,雖然私有成員Stream.formatStream.format1之間的唯一區別只是o說法,而且這是方法本身使用。

編譯器如何以不同的方式處理兩個幾乎相同的方法?

編輯:感謝您的解釋和抱歉的無知。

+0

多少次迭代並運行,以獲得時序?一?一百萬?您需要使您的示例足夠寬泛,以平滑緩存效果,內核調度等。 – spraff 2012-01-12 16:03:50

回答

8

問題是,當您只使用一個參數調用Format1時,它只會返回一個函數。它並沒有執行實際的格式。這意味着,如果你比較的性能:

Stream.Format 42 
Stream.Format1 42 

...那麼你實際上是比較實際的格式的性能(即創建數組和寫入的東西),在第一種情況和性能代碼只是簡單地返回一個函數值而不做任何事情。

如果你不使用format1o參數作爲任何事情,那麼你可以傳遞一些虛擬值來實際評估函數並得到結果。那麼你應該得到類似的性能:

Stream.Format 42 
Stream.Format1 42() 
4

Format實際上調用Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a)

Format1返回一個函數,當傳遞一個對象時調用Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a)

即,一方做實際工作,另一方只是部分功能應用;後者明顯更快。

如果你不熟悉的部分功能的應用,有一個標題爲「參數的部分應用程序」這是值得一讀了在F#文檔的一部分:Functions (F#)

相關問題