2009-09-16 6 views
5

、私は疑問に思っ:F番号:部分的な組み合わせと最適化を組み合わせることが可能であった場合は、今日の私のコード内の関数を見てみると一部のアプリケーションおよび事前計算

基本的に
let foo (X:float) y1 y2 dx = 
    y1 + (y2 - y1) * dx/X 

を、ちょうど比適用 - その最初の3つのパラメータを所与のループ内では一般に同じである。

私はこれをしなかった場合、私は多分考えた:

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

F#は賢い取得し、私は部分的に最初の3つのパラメータを適用するときに私のために最適化するが、それはモードをデバッグするだろう、ケースのように表示されません。 (私は正しい方法でテストするつもりはないとは思うが)。

質問は、これは問題ありませんか?そして、もしそうでないならば、それを実行する良い方法があります(2つのパラメータを持つ別の関数を書く以外に)?

答えて

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"。これはコンパイラを最適化するために、 "Expensive"に置くことができます(私の説明の例とは異なり、印刷されなかったと仮定します)。これは、Haskellのように、すべてが純粋でコンパイラ/ランタイムが関数呼び出しを 'キャッシュ'することができる理由です。 私の個人的な意見は、システムが「魔法のように最適化する」ことを期待していることです。これはパイプの夢です。コードを高速にしたい場合は、氏のコンピュータにステップごとに記入してください。人間はいつも努力しなければならない。 – Brian

+0

あなたのボーレートは私の脳のものとまったく同じです:) – Benjol

1

デバッグモードで明らかに異なるものは何もないことは驚くことではありません。実際にN回の繰り返しを繰り返すのはなぜですか(#time;;、F#インタラクティブプロンプト)。すべてが、DXの固定値のための共通の計算を共有するためにあなたの希望については

、これを試してみてください。ある

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

は、FDXは、部分的なアプリケーションです。ループの外側に明示的に格納すると、オプティマイザからさらに多くのものを得ることができます。

少なくともインタラクティブなプロンプト(私は完全な最適化がそこで行われているとは思わない?)は、私の提案がわずか15%のスピードアップであるように見える(それは間違いなくスピードアップがあるfoo2の全身)。これを行うと、はるかに高速です:

foo3は(Benjlolから)である
let fdx = foo3 X y1 y2 
for dx in dxes do 
    fdx dx 

:ちょうどループで4引数の関数としてfoo3使用

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

注foo2はの倍の遅いですが、保存しますループ外の部分的なアプリケーションは、3倍高速です。

3

(これが評判whoringではありません、私は私の質問を始めたとき、私は正直にこれを考えていなかった)

ここで私が作ってみた一つの解決策、それは最高のかどうかわからないです:

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

非常に速く動作します。

+0

これは私の答えと同じでなければなりません。これはカルト関数の部分的なアプリケーション(F#デフォルト)がどのように機能するかです。 –

+0

Ok ...最適化されていない方が高速です。奇妙な。それは修正する必要があります。 –

+0

@ wrang-wrang、これは、効果が並べ替えられているので、カルト関数の単純な部分的な適用と同じではありません。関数の先頭に "fun dx - >"を置いた簡単なイータ変換は同等ですが、他のコードの後に​​ "fun dx - >"を移動すると、最初の2つの引数が適用された後にもう一方のコードが一度実行されます。 (効果がない場合、これは区別できませんが、存在するとその違いは明らかです。) – Brian

関連する問題