FizzBuzz程序似乎很慢:为什么?

huangapple go评论81阅读模式
英文:

FizzBuzz program seems slow: why?

问题

Go语言的标准输出不进行缓冲。如果切换到缓冲版本并手动刷新,它会更接近你的预期。避免使用fmt可以使其运行得更快。

我正在尝试用Go编写FizzBuzz程序。

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到一百万的数字时,它需要不到一秒的时间来完成。
当我用C、Rust、Haskell或Python编写等效的程序时,它需要的时间从半秒(Python)到零秒(Rust和Haskell)不等。

这是预期的吗?还是我忽略了一些Go的技巧?为什么Go似乎比其他语言慢?

【编辑】

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

看起来100%的时间都花在了fmt.(*fmt).fmt_complex上,我猜这与Println有关。我还尝试了使用strconv.Itoa而不是fmt.Sprint,虽然性能略有提升(约0.2秒),但基本结果相同。

是打印输出慢吗?如果是,为什么?

【编辑】

对于jgritty,这是等效的Python程序和计时结果。
我对打印输出为什么较慢感兴趣。Go是否在幕后做了一些我不知道的事情?

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 str(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
英文:

[ANSWER] Go doesn't buffer stdout. Switching to a buffered version and manually flushing brings it much closer to what you would expect. Avoiding fmt makes it run as fast as you like.

I'm trying to write the FizzBuzz program in Go.

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

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

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

When I run it for numbers from 1 to a million it takes just under a second to complete.
When I write the equivalent program in C, Rust, Haskell or Python it takes anywhere from half a second (Python) to zero seconds (Rust and Haskell).

Is this to be expected, or am I missing some Go-fu? Why does the go seem slower than the other languages?

[EDIT]

Running with the profiler as suggested by Robert Harvey.

It looks like 100% of the time is spent in fmt.(*fmt).fmt_complex, which I'm guessing is related to the Println(?). Also tried the program with strconv.Itoa instead of the fmt.Sprint and I get the slight performance increase (~0.2s) but the same basic results.

Is it the printing that's slow and if so why?

[EDIT]

For jgritty the equivalent Python program and timings.
I'm interested in why the printing is slower? Is go doing something behind the scenes I'm not aware of?

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

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

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

main()
$ time pypy3 fizzbuzz.py &gt;/dev/null

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

答案1

得分: 6

标准输出在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 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)
    }
}
英文:

The standard output is buffered in Python and C, but not Go. Buffer the output for an apples to apples comparison. This almost cut the time in half on my laptop.

import (
    &quot;bufio&quot;
    &quot;fmt&quot;
    &quot;os&quot;
)

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

Eliminate the use of the fmt package for another improvement:

package main

import (
    &quot;bufio&quot;
    &quot;os&quot;
    &quot;strconv&quot;
)

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

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

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

huangapple
  • 本文由 发表于 2014年10月2日 04:22:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/26149914.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定