4

我正在用 C、Go 和 Python 解决一个简单的问题并比较结果。if解决方案只是假设在-else块内有两个方程。以下是我的解决方案的代码:

C

Python

我在比较这三种语言处理浮点结果的方式,因此制作了这个脚本来生成测试用例,然后比较结果,一次两个。

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

就运行时间而言,我认为 Go 将介于 C 和 Python 之间。我的 Go 代码是否效率低下,如果是这样,是什么?

PS 我也运行了上面三个程序,没有进行文件处理操作,结果还是一样,即 Go 比其他两个程序花费了非常长的时间。

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.524544608s 秒内读取数据
在 10.083329ms 秒内处理数据
1.703542918s秒输出处理数据

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

更新:例如,包装inputoutputwithbufio的读者和作家:

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

并且使用binputboutput用于缓冲 I/O,您的原始版本在我的机器上运行时间为 2.1 秒,比 Python 的 2.7 快一些。

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

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

  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 执行异步请求并将控制权返回给调度程序。调度程序寻找另一个等待控制的 goroutine,但只有一个等待 io。所以调度程序循环什么都不做。

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

于 2013-06-20T08:41:23.493 回答