为什么我的Go程序在以下场景中的表现比预期的要糟糕得多?

时间:2013-06-20 08:26:52

标签: python c performance go

我在C,Go和Python中解决了一个简单的问题并比较了结果。解决方案应该只是在if - else块内有两个方程式。以下是我的解决方案的代码:

C

python

go

我正在比较三种语言处理浮点结果的方式,因此使this脚本生成测试用例,并this比较结果,一次两个。

奇怪的是运行前3个脚本所需的时间。 C程序显然可以在几秒钟内运行。 Python需要大约2.5-3秒。但是,Go会花费大约24-25秒来运行generate_test_cases.py脚本生成的测试用例上的程序。

就运行时间而言,我认为Go会介于C和Python之间。我使用Go代码做了一些效率低下的事情,如果有的话,是什么?

P.S。我还运行了上述三个没有文件处理操作的程序,结果仍然相同,即Go与其他两个相比需要非常长的时间。

2 个答案:

答案 0 :(得分: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")
}

运行它产生了这个输出:

Opened files in  2.011228ms seconds
Made array in  109.904us seconds
Read data in  4.524544608s seconds
Processed data in  10.083329ms seconds
Output processed data in  1.703542918s seconds

所以看来在我的机器上,所有的数学运算都发生在大约10ms,但是I / O很慢,证实了这个假设。正如Janne在评论中指出的那样,选项可能比fmt更快。

更新:例如,将inputoutputbufio的读者和作者包装在一起:

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

并使用binputboutput进行缓冲I / O,您的原始版本在我的机器上运行2.1秒,比Python的2.7更快。

更新2:我注意到我只是通过切换到缓冲的I / O获得了不同的结果。

  1. 事实证明,您还需要调整格式字符串以包含\n,就像在C版中一样。我认为这实际上更正确,但看起来你可以在没有缓冲的情况下使用它。

  2. Flush()你的缓冲输出也很重要,我之前没有提到过。

  3. 这是我完整的缓冲解决方案:

    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")
    }
    

答案 1 :(得分:1)

我想以下行在go中运行得比较慢。

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

当你做IO时,Go会发生魔力。它们看起来像是同步的,但实际上它们是异步的。 Go rotine执行异步请求并将控制权返回给调度程序。调度程序寻找另一个等待控制的goroutine,但只有一个等待io。 因此调度程序循环无效。

如果您有2个,10个或100个并发goroutine,那么将会看到更好的性能。