2014-10-01 8 views
7

[ANSWER] Go non bufferizza lo stdout. Passare a una versione con buffer e lo svuotamento manuale lo avvicina molto di più a quello che ti aspetteresti. Evitare fmt lo fa correre veloce come preferisci.Il programma FizzBuzz sembra lento: perché?

Sto provando a scrivere il programma FizzBuzz in 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) 
    } 
} 

Quando lo eseguo per i numeri da 1 a un milione ci vuole poco meno di un secondo per completare. Quando scrivo il programma equivalente in C, Rust, Haskell o Python ci vuole da mezzo secondo (Python) a zero secondi (Ruggine e Haskell).

E 'normale o mi manca qualche Go-Fu? Perché il go sembra più lento delle altre lingue?

[EDIT]

Correndo con il profiler come suggerito da Robert Harvey.

Sembra che il 100% del tempo sia trascorso in fmt. (* Fmt) .fmt_complex, che suppongo sia correlato al Println (?). Ho anche provato il programma con strconv.Itoa al posto di fmt.Sprint e ottengo il leggero aumento delle prestazioni (~ 0,2 s) ma gli stessi risultati di base.

È la stampa che è lenta e in tal caso perché?

[EDIT]

Per jgritty il programma Python equivalente e tempi. Mi interessa sapere perché la stampa è più lenta? Vai a fare qualcosa dietro le quinte di cui non sono a conoscenza?

$ 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

Nota che Haskell è un linguaggio pigro; probabilmente non sta valutando i risultati finché non chiedi effettivamente l'output. Lo stesso è probabilmente vero per Rust. –

+0

Forse ci vuole più tempo per avviarsi rispetto a quelle di altre lingue? Cosa succede se hai provato a fare in modo che il programma elabori più dati in modo che siano necessari circa 10 secondi? –

risposta

7

L'output standard è memorizzato in Python e C, ma non Go. Buffer l'output per un confronto tra mele e mele. Questo quasi ha tagliato il tempo a metà sul mio portatile.

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

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

Eliminare l'uso del fmt package per un altro miglioramento:

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

Suppongo tu voglia dire fmt.Fprintln invece di Println. – Joseph

+0

Questo mi sembra un enorme miglioramento delle prestazioni! Quattro volte accelerano, e siamo giù a un quarto di secondo. – Joseph

+2

Interessante che Go decida di non bufferizzare lo stdout di default: qual è la logica? – Joseph