2013-06-20 68 views
4

我正在用C,Go和Python解决一个简单的问题,并比较结果。解决方案只需要在if - else区块中有两个等式。以下是我的解决方案的代码:为什么我的Go程序在以下情况下性能比预期差得多?

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比另外两个时间花费了非常长的时间。

+0

相关转到问题:[大UTF-8字符串的快速扫描(http://stackoverflow.com/q/14924598/222914) –

回答

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的读者和作家:使用binputboutput用于缓冲I/O,你原来的版本2.1秒运行一次就

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

和我机器,比Python 2.7稍快。

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

  1. 事实证明,您还需要调整您的格式字符串包括\n,当你在C版本做。我认为这种方式实际上更为正确,但看起来你可以在没有缓冲的情况下摆脱它。

  2. 这对你的缓冲输出也是很重要的,我曾经做过但之前没有提到过。

这里是我完整的缓冲溶液:

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

感谢您的分析! – pymd

+2

@nomad没问题;在我失眠的回合中一个有趣的分流。我添加了一个更新,显示如何缓冲您的I/O,使结果与您最初期望的C> Go> Python速度相匹配。 –

+0

太棒了!我今天学到了一些不错的技巧。 :) 再次感谢! – pymd

1

我想下面的线路工作得更慢。

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

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

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

+0

那么这意味着如果没有goroutines,go实际上比python慢​​? – pymd

+1

如果你有1个线程vs 1个gorotine,并且你在循环中执行了'fmt.Fscanf(input,“%f”,&p)',那么不要做任何其他的事情。但是在其他任务中,python可能会更快。我猜你可能会把Go与[Stackless Python](http://www.stackless.com/) – Max

+0

相提并论!非常感谢答案和无堆栈的python链接! – pymd

相关问题