2012-05-06 9 views
8

私はF#を学んでいます。私はこの言語について私を夢中にしているのはパフォーマンスです。私は、慣用的なF#を同じ言語で書かれた命令的スタイルのコードと比較する小さなベンチマークを書いた。そして驚いたことに、機能的なバージョンはかなり速く出てくる。Seq.mapは通常のforループより速いですか?

ベンチマークで構成されていますFile.WriteAllLinesを使用して同じ結果をファイルに書き戻す各行内の文字の順序

  • を逆

    1. File.ReadAllLines
    2. を使用して、テキストファイルを読み込みます。

    は、ここでは、コードです:

    open System 
    open System.IO 
    open System.Diagnostics 
    
    let reverseString(str:string) = 
        new string(Array.rev(str.ToCharArray())) 
    
    let CSharpStyle() = 
        let lines = File.ReadAllLines("text.txt") 
        for i in 0 .. lines.Length - 1 do 
         lines.[i] <- reverseString(lines.[i]) 
    
        File.WriteAllLines("text.txt", lines) 
    
    let FSharpStyle() = 
        File.ReadAllLines("text.txt") 
        |> Seq.map reverseString 
        |> (fun lines -> File.WriteAllLines("text.txt", lines)) 
    
    let benchmark func message = 
        // initial call for warm-up 
        func() 
    
        let sw = Stopwatch.StartNew() 
        for i in 0 .. 19 do 
         func() 
    
        printfn message sw.ElapsedMilliseconds 
    
    
    [<EntryPoint>] 
    let main args = 
        benchmark CSharpStyle "C# time: %d ms" 
        benchmark FSharpStyle "F# time: %d ms" 
        0 
    

    どのようなファイルのサイズ、「F#スタイル」バージョンは、「C#スタイル」バージョンの時間の約75%で完了します。私の質問は、それはなぜですか?命令版では明らかな非効率性は見られません。

  • +1

    Kudos @Dr_Asikよく準備された質問です。 –

    答えて

    10

    Seq.mapは、Array.mapとは異なります。シーケンス(IEnumerable<T>)は列挙されるまで評価されないので、F#スタイルコードではによって生成されたシーケンス(配列ではない)を介してFile.WriteAllLinesループまで実際には計算が行われません。

    つまり、C#スタイルのバージョンでは、すべての文字列を反転して、逆の文字列を配列に格納してから、配列をループしてファイルに書き出します。 F#スタイルのバージョンでは、すべての文字列を反転してファイルに多かれ少なかれ直接書き込んでいます。これは、F#スタイルのコードがファイル全体を2回だけループしている間に、C#スタイルのコードがファイル全体を3回ループしていることを意味します(配列への読み込み、逆配列の構築、ファイルへの配列の書き込み)ファイルの行を逆にします)。

    あなたがFile.ReadLinesの代わりSeq.mapと組み合わせFile.ReadAllLinesを使用した場合は、すべての最高のパフォーマンスを得るだろう - しかし、あなたの出力ファイルがまだから読みながらあなたは出力に書き込むことだろうとして、あなたの入力ファイルとは別のでなければならないであろう入力。

    +1

    F#バージョンがFile.WriteAllLines(string、IEnumerable )を呼び出す間、C#バージョンはFile.WriteAllLines(string、string [])を呼び出します。したがって、実際には3つではなく2つのループがあります。その方法の他のオーバーロードがあったことは私の心には達しませんでした。説明ありがとう! – Asik

    1

    Seq.mapフォームには、通常のループよりもいくつかの利点があります。関数リファレンスを1回だけ事前計算することができます。変数の代入を避けることができます。入力シーケンス長を使用して結果配列をプリサイズすることができます。

    +1

    それは非常に有効なポイントのように見えますが、私はあなたが何を意味するのが見苦しいですか。拡大して各ポイントを少し説明してください。ありがとう。 – Asik

    関連する問題