2014-10-01 4 views
7

[ANSWER] Go не выполняет буферизацию stdout. Переход на буферизованную версию и ручную очистку приближает ее к тому, что вы ожидаете. Избегание fmt заставляет его работать так быстро, как вам нравится.Программа FizzBuzz кажется медленной: почему?

Я пытаюсь написать программу FizzBuzz в Go.

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 and Haskell).

Ожидается ли это, или я пропустил какой-нибудь Go-fu? Почему ход кажется медленнее, чем другие языки?

[EDIT]

Запуск с профилировщика как предложил Роберт Харви.

Похоже, что 100% времени тратится на fmt. (* Fmt) .fmt_complex, который, как я предполагаю, связан с Println (?). Также попробовал программу с strconv.Itoa вместо fmt.Sprint, и я получил небольшое увеличение производительности (~ 0,2 с), но те же основные результаты.

Это медленная печать, и если да, то почему?

[EDIT]

Для 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 
+0

http://blog.golang.org/profiling-go-programs –

+0

Обратите внимание, что Haskell является ленивым языком; это, вероятно, не оценивает ваши результаты, пока вы на самом деле не спросите о выходе. То же самое, скорее всего, относится к Rust. –

+0

Возможно, потребуется больше времени для запуска, чем другие языки? Что делать, если вы попробовали сделать программу более объемной, поэтому для ее выполнения требуется около 10 секунд? –

ответ

7

Стандартный выход буферизуется в 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) 
    } 
} 
+0

Я предполагаю, что вы имеете в виду fmt.Fprintln вместо Println. – Joseph

+0

Это, кажется, дает мне значительное улучшение производительности! В четыре раза ускоряется, и мы сокращаемся до четверти секунды. – Joseph

+2

Интересно, что Go решил не буферировать stdout по умолчанию: в чем причина? – Joseph

Смежные вопросы