【问题标题】:FizzBuzz program seems slow: why?FizzBu​​zz 程序似乎很慢:为什么?
【发布时间】:2014-11-26 19:09:29
【问题描述】:

[ANSWER] Go 不缓冲标准输出。切换到缓冲版本并手动刷新使其更接近您的预期。避免 fmt 可以让它随心所欲地运行。

我正在尝试用 Go 编写 FizzBu​​zz 程序。

func main() {
  for i := 1; i <= 1000000; i++ {
    fmt.Println(fizzbuzz(i))
  }
}

func fizzbuzz(n int) string {
  fizzy := n%3 == 0
  buzzy := n%5 == 0

  switch {
  case fizzy && buzzy:
    return "FizzBuzz"
  case fizzy:
    return "Fizz"
  case buzzy:
    return "Buzz"
  default:
    return fmt.Sprint(n)
  }
}

当我对从 1 到 100 万的数字运行它时,只需不到一秒的时间即可完成。 当我用 C、Rust、Haskell 或 Python 编写等效程序时,它需要半秒(Python)到零秒(Rust 和 Haskell)。

这是意料之中的,还是我错过了一些 Go-fu?为什么 go 看起来比其他语言慢?

[编辑]

按照 Robert Harvey 的建议使用分析器运行。

看起来 100% 的时间都花在了 fmt.(*fmt).fmt_complex,我猜这与 Println(?) 有关。还尝试了使用 strconv.Itoa 而不是 fmt.Sprint 的程序,我得到了轻微的性能提升(~0.2s),但基本结果相同。

是打印速度慢吗?如果是,为什么?

[编辑]

对于 jgritty,等效的 Python 程序和计时。 我对为什么打印速度较慢感兴趣?是在做我不知道的幕后工作吗?

$ cat fizzbuzz.py
def fizzbuzz(n):
    fizzy = n%3 == 0
    buzzy = n%5 == 0

    if fizzy and buzzy:
        return "FizzBuzz"
    elif fizzy:
        return "Fizz"
    elif buzzy:
        return "Buzz"
    else:
        return ("%u" % n)

def main():
    for i in range(1, 10**6):
        print(fizzbuzz(i))

main()
$ time pypy3 fizzbuzz.py >/dev/null

real    0m0.579s
user    0m0.545s
sys     0m0.030s

【问题讨论】:

  • 请注意,Haskell 是一种惰性语言;在您实际要求输出之前,它可能不会评估您的结果。 Rust 很可能也是如此。
  • 也许 go 的启动时间比其他语言要长?如果您尝试让程序处理更多数据以使其运行大约需要 10 秒呢?

标签: performance go fizzbuzz


【解决方案1】:

标准输出在 Python 和 C 中缓冲,但不是 Go。缓冲苹果与苹果比较的输出。这几乎将我在笔记本电脑上的时间缩短了一半。

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    w := bufio.NewWriter(os.Stdout)
    for i := 1; i <= 1000000; i++ {
         fmt.Fprintln(w, fizzbuzz(i))
    }
    w.Flush()
}

取消使用fmt package 以进行另一项改进:

package main

import (
    "bufio"
    "os"
    "strconv"
)

func main() {
    w := bufio.NewWriter(os.Stdout)
    for i := 1; i <= 1000000; i++ {
        w.WriteString(fizzbuzz(i))
        w.WriteString("\n")
    }
    w.Flush()
}

func fizzbuzz(n int) string {
    fizzy := n%3 == 0
    buzzy := n%5 == 0

    switch {
    case fizzy && buzzy:
        return "FizzBuzz"
    case fizzy:
        return "Fizz"
    case buzzy:
        return "Buzz"
    default:
        return strconv.Itoa(n)
    }
}

【讨论】:

  • 我假设您的意思是 fmt.Fprintln 而不是 Println。
  • 这似乎给我带来了巨大的性能提升!速度提高了四倍,而我们的速度降低到了四分之一秒。
  • 有趣的是,Go 决定默认不缓冲标准输出:理由是什么?
  • 这运行得非常快! real 0m0.047suser 0m0.043ssys 0m0.003s
  • @Joseph(在一个不太有用的注释上,这样的微基准测试,非常超调,哇。;))
猜你喜欢
  • 2012-03-16
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
  • 1970-01-01
  • 1970-01-01
  • 2017-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多