4

C、Go、Python で簡単な問題を解いて結果を比較していました。解は単純にif-elseブロック内に 2 つの方程式を持つことになっています。以下は、私のソリューションのコードです。

パイソン

行く

私は 3 つの言語が浮動小数点の結果を処理する方法を比較していたので、このスクリプトを作成してテスト ケースを生成し、これを一度に 2 つずつ結果を比較しました。

奇妙なのは、最初の 3 つのスクリプトを実行するのにかかる時間です。C プログラムは明らかに数秒以内に実行されます。Python は約 2.5 ~ 3 秒かかります。generate_test_cases.pyただし、Go は、スクリプトによって生成されたテスト ケースでプログラムを実行するのに約 24 ~ 25 秒かかります。

実行時間に関する限り、Go は C と Python の間のどこかにあると思いました。Go コードで何か非効率なことをしていますか?もしそうなら、何をしますか?

PS また、上記の 3 つのプログラムをファイル処理操作なしで実行しましたが、結果は同じでした。つまり、Go は他の 2 つに比べて非常に長い時間がかかりました。

4

2 に答える 2

4

Max のように、Go の遅さは I/O パフォーマンスの低さに関係しているのではないかと強く疑っていました。私はこの仮説をテストしました:

package main

import "fmt"
import "os"
import "time"

func main(){
    now := time.Now()
    input,_ := os.Open("testing/test_cases.txt")
    defer input.Close()
    output,_ := os.Create("testing/Goutput.txt")
    defer output.Close()

    var ncases int
    var p float64
    fmt.Fscanf(input,"%d",&ncases)

    fmt.Println("Opened files in ", time.Since(now), "seconds")
    now = time.Now()

    cases := make([]float64, ncases)
    fmt.Println("Made array in ", time.Since(now), "seconds")
    now = time.Now()

    for i := 0; i < ncases; i++ {
        fmt.Fscanf(input,"%f",&cases[i])
    }

    fmt.Println("Read data in ", time.Since(now), "seconds")
    now = time.Now()

    for i := 0; i < ncases; i++ {
        p = cases[i]
        if p >= 0.5 {
            cases[i] = 10000 * (1-p) * (2*p-1) + 10000
        } else {
            cases[i] = p*(1-2*p)*10000 + 10000
        }
    }

    fmt.Println("Processed data in ", time.Since(now), "seconds")
    now = time.Now()

    for i := 0; i < ncases; i++ {
        fmt.Fprintln(output, cases[i])
    }

    fmt.Println("Output processed data in ", time.Since(now), "seconds")
}

実行すると、次の出力が生成されました。

2.011228ms でファイルを開きました
109.904us 秒でアレイを作成
4.524544608 秒でデータを読み取る
10.083329ms 秒でデータを処理
1.703542918秒で処理済みデータを出力

したがって、私のマシンでは、すべての計算が約 10 ミリ秒で行われるように見えますが、I/O は遅く、仮説を裏付けています。Janne がコメントで指摘したように、より高速なオプションが存在する可能性がありますfmt

更新:たとえば、のリーダーinputとライターoutputを使用したラッピング:bufio

binput := bufio.NewReader(input)
boutput := bufio.NewWriter(output)

およびバッファリングされた I/O を使用するbinputboutput、元のバージョンは私のマシンで 2.1 秒で実行され、Python の 2.7 よりもいくらか高速です。

更新 2:バッファー I/O に切り替えるだけで、異なる結果が得られることに気付きました。

  1. \nC バージョンの場合と同様に、フォーマット文字列を調整して を含める必要があることもわかりました。これは実際にはどちらの方法でも正しいと思いますが、バッファリングされていない間はうまくいくようです。

  2. Flush()これは、バッファリングされた出力にとっても重要です。

これが私の完全なバッファリングされたソリューションです:

package main

import "fmt"
import "os"
import "bufio"
import "time"

func main(){
    now := time.Now()

    nbinput, _ := os.Open("testing/test_cases.txt")
    defer nbinput.Close()

    nboutput, _ := os.Create("testing/Goutput.txt")
    defer nboutput.Close()

    binput := bufio.NewReader(nbinput)
    boutput := bufio.NewWriter(nboutput)

    var ncases int
    var gain, p float64
    fmt.Fscanf(binput,"%d\n",&ncases)

    for i := 0; i < ncases; i++ {
        fmt.Fscanf(binput, "%f\n", &p)
        if p >= 0.5 {
            gain = 10000 * (1-p) * (2*p -1)
        } else {
            gain = p*(1-2*p)*10000
        }
        fmt.Fprintln(boutput, gain+10000)
    }
    boutput.Flush()
    fmt.Println("Took ", time.Since(now), "seconds")
}
于 2013-06-20T09:07:48.610 に答える
1

次の行は、goでは遅く動作すると思います。

    fmt.Fscanf(input,"%f",&p)
    fmt.Fprintln(output,gain+10000)

IO を実行すると、Go の魔法が起こります。同期のように見えますが、実際には非同期です。Go rotine は非同期リクエストを実行し、制御をスケジューラに返します。スケジューラーは、制御を取得するために待機している別のゴルーチンを探しますが、待機している io は 1 つしかありません。したがって、スケジューラは何もせずにループします。

2、10、または 100 の同時ゴルーチンがある場合は、パフォーマンスが向上します。

于 2013-06-20T08:41:23.493 に答える