2009-09-16 24 views
5

綜觀今天我的代碼的功能,我想知道是否有可能部分組合和優化組合:F#:局部應用的預先計算

let foo (X:float) y1 y2 dx = 
    y1 + (y2 - y1) * dx/X 

基本上,只要施加一個比 - 這樣的前三個參數在給定的循環內通常是相同的。

我想也許如果我只是這樣做:

let foo2 (X:float) y1 y2 dx = 
    let dy = (y2 - y1)/X 
    y1 + dy * dx 

F#會弄巧,優化對我來說,當我部分申請前三個參數,但調試模式下,它不會出現這樣的情況(儘管我不確定我是否以正確的方式進行了測試)。

問題是,應該這樣工作嗎?如果不是,有沒有更好的方式來做到這一點(除了用兩個參數編寫另一個函數外)?

回答

4

我認爲大多數這樣的'魔法優化'需要'效果分析',只能通過神祕的'足夠聰明的編譯器'來完成。

思考這樣的:

let Expensive x y = 
    printfn "I am a side-effect of Expensive" 
    x + y // imagine something expensive 

let F x y z = 
    let tmp = Expensive x y 
    z + tmp 

printfn "Least chance of magic compiler optimization" 
for i in 1..3 do  
    F 3 4 i 

printfn "Slightly better chance" 
let Part = F 3 4 
for i in 1..3 do  
    Part i 

printfn "Now for real" 
let F2 x y = 
    let tmp = Expensive x y 
    (fun z -> z + tmp) 

printfn "Of course this still re-does it" 
for i in 1..3 do  
    F2 3 4 i 

printfn "Of course this finally saves re-do-ing Expensive" 
let Opt = F2 3 4 
for i in 1..3 do  
    Opt i 

(* output 

Least chance of magic compiler optimization 
I am a side-effect of Expensive 
I am a side-effect of Expensive 
I am a side-effect of Expensive 
Slightly better chance 
I am a side-effect of Expensive 
I am a side-effect of Expensive 
I am a side-effect of Expensive 
Now for real 
Of course this still re-does it 
I am a side-effect of Expensive 
I am a side-effect of Expensive 
I am a side-effect of Expensive 
Of course this finally saves re-do-ing Expensive 
I am a side-effect of Expensive 

*)  

的一點是,關於效果的語言語義需要編譯器的行爲完全一樣,除非「昂貴」沒有效果,編譯器是真的很聰明,可以發現在其自己的。

+0

另外,這就是爲什麼人們需要例如.Net中的「PureAttribute」,你可以把它放在'昂貴'(假設它不打印,不像我的展示例子),以便將編譯器推進到這種優化中。或者,這就是人們喜歡Haskell的原因,一切都是純粹的,編譯器/運行時可以「緩存」任何函數調用。 在這一天結束時,我個人的觀點是希望系統能夠「神奇地優化」這個,因爲你是一個夢想。如果你想讓你的代碼變得更快,請逐步向Computer先生詳細說明。人類必須始終努力工作。 – Brian 2009-09-16 07:46:24

+0

你的這個波特率和我的大腦完全一樣:) – Benjol 2009-09-16 08:16:25

1

我並不感到驚訝,在調試模式下沒有任何顯然不同。爲什麼不實際重複N次(在F#交互提示下爲#time;;)。

至於你希望分享爲所有,但DX的固定值的通用計算,試試這個:

let fdx = foo2 X y1 y2 
for dx in dxes do 
    fdx dx 

也就是說,FDX是部分應用程序。明確地將其存儲在循環之外使我希望優化器可以獲得更多。

至少在交互式提示符下(我認爲完全沒有完成優化,是嗎?)看起來我的建議只有15%的加速(奇怪的是有任何加速,因爲它肯定會重複foo2的全身)。這樣做是要快得多:

let fdx = foo3 X y1 y2 
for dx in dxes do 
    fdx dx 

其中foo3是(從Benjlol):

let foo3 (X:float) y1 y2 = 
    let dy = (y2 - y1)/X 
    (fun dx -> y1 + dy * dx) 

注意,只是用foo3在迴路中的4參數函數的兩倍,foo2的慢,但存儲在循環外的部分應用程序,它快了3倍。

3

(這不是名聲嫖娼,我真的沒有想到這一點時,我開始問我的問題)

這裏有一個解決方案,我想出來的,不知道這是否是最好的:

let foo3 (X:float) y1 y2 = 
    let dy = (y2 - y1)/X 
    (fun dx -> y1 + dy * dx) 

工程速度更快。

+0

這應該和我的答案一樣。這就是curried函數(F#默認)的部分應用程序的工作原理。 – 2009-09-16 07:29:08

+0

好的...它在非優化模式下速度更快。奇怪的。這應該是固定的。 – 2009-09-16 07:35:18

+0

@ wrang-wrang,這與curried函數的簡單部分應用不同,因爲效果已經重新排序。在函數頂部留下「fun dx - >」的簡單eta轉換將是等價的,但在其他代碼之後移動「fun dx - >」意味着其他代碼在應用前兩個參數後運行一次。 (在沒有效果的情況下,這是無法區分的,但在它們的存在下,差異是明顯的。) – Brian 2009-09-16 07:53:38

相關問題