2014-10-01 3 views
7

[ODPOWIEDŹ] Go nie buforuje stdout. Przejście do wersji buforowanej i ręczne spłukiwanie znacznie przybliża to, czego można się spodziewać. Unikanie fmt sprawia, że ​​działa tak szybko, jak chcesz.Program FizzBuzz wydaje się powolny: dlaczego?

Próbuję napisać program FizzBuzz w 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) 
    } 
} 

Po uruchomieniu go dla liczb od 1 do miliona, ukończenie zajmuje mniej niż sekundę. Kiedy piszę program równoważny w C, Rust, Haskell lub Python, zajmuje to od pół sekundy (Python) do zera sekund (Rust i Haskell).

Czy można się tego spodziewać, czy też brakuje mi Go-fu? Dlaczego wydaje się wolniej niż inne języki?

[EDIT]

Biegając z profilera sugerowane przez Robert Harvey.

Wygląda na to, że 100% czasu spędzamy w fmt. (* Fmt) .fmt_complex, który, jak przypuszczam, jest powiązany z Println (?). Wypróbowałem również program z strconv.Itoa zamiast fmt.Sprint i otrzymałem niewielki wzrost wydajności (~ 0.2s), ale te same podstawowe wyniki.

Czy drukowanie jest powolne, a jeśli tak, dlaczego?

[Edycja]

Na jgritty równoważnej Pythonowego programu i czas podawania. Interesuje mnie, dlaczego drukowanie jest wolniejsze? Czy robię coś poza kulisami, o których nie wiem?

$ 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

Zauważ, że Haskell jest językiem leniwy; prawdopodobnie nie ocenia twoich wyników, dopóki nie poprosisz o wynik. To samo najprawdopodobniej dotyczy Rust. –

+0

Może zacząć działać dłużej niż w innych językach? Co jeśli próbowałeś, aby program przetwarzał więcej danych, więc uruchomienie trwa 10 sekund? –

Odpowiedz

7

Standardowe wyjście jest buforowane w Pythonie i C, ale nie w Go. Buforuj wynik dla porównania jabłek z jabłkami. To prawie skróciło czas w połowie na moim laptopie.

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

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

Wyeliminowanie wykorzystanie fmt package dla innego poprawy:

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

Zakładam, że masz na myśli fmt.Fprintln zamiast Println. – Joseph

+0

Wydaje mi się, że poprawi mi to wydajność! Cztery razy przyspieszają, a my jesteśmy o ćwierć sekundy. – Joseph

+2

Interesujące, że Go postanowił nie buforować domyślnie stdout: jakie jest uzasadnienie? – Joseph