Kamori
Kamori

Reputation: 356

Why does my python print run faster than Go's fmt.Print and os.Stdout.Write

I'm trying to research some details about efficiency with Go because I'm new to it. One of the tests I wanted to perform was to see if Go's fmt.Print was faster or slower than os.Stdout.WriteLine

Here are the results.

fmt.Print speed

[kamori@kamori-pc playground]$ cat print.go 
package main

import "fmt"
import "os"

func main(){
   os.Stderr.WriteString("Printing Hello World! 1000000000 times")
   for i := 0; i < 1000000000; i++{
       fmt.Print("Hello World!\n")
   }
}
[kamori@kamori-pc playground]$ go build print.go
[kamori@kamori-pc playground]$ time ./print >/dev/null
Printing Hello World! 1000000000 times
real    13m9.127s
user    8m52.461s
sys 4m15.433s


os.Stdout.WriteString

[kamori@kamori-pc playground]$ cat stdoutwrite.go 
package main

import "os"

func main(){
   os.Stderr.WriteString("StdoutWriting Hello World! 1000000000 times")
    for i := 0; i < 1000000000; i++{
       os.Stdout.WriteString("Hello World!\n")
   }
}
[kamori@kamori-pc playground]$ go build stdoutwrite.go 
[kamori@kamori-pc playground]$ time ./stdoutwrite >/dev/null
StdoutWriting Hello World! 1000000000 times
real    11m36.198s
user    7m31.211s
sys    4m0.080s

I understand this isn't the best way to test this process. But it was the one I chose and it was interesting. I'm still curious to know the differences but the results kinda make sense to me. However, just for fun, I did the same thing with Python 3 and it went way faster

Python 3

[kamori@kamori-pc playground]$ cat print.py

for i in range(1000000000):
    print("Hello World!")
[kamori@kamori-pc playground]$ time python3 print.py >/dev/null

real    6m46.542s
user    6m44.450s
sys    0m0.836s

Here is version details

[kamori@kamori-pc playground]$ go version
go version go1.13.4 linux/amd64
[kamori@kamori-pc playground]$ python --version
Python 3.7.4
[kamori@kamori-pc playground]$ uname -a
Linux kamori-pc 4.19.59-rt23-MANJARO #1 SMP PREEMPT RT Sat Jul 20 07:14:03 UTC 2019 x86_64 GNU/Linux

My question is, why did python out perform Go? My expectation was Go would be faster as its a compiled language. Sure there isn't a ton of logic that needs to be figured out on the fly so Python might be able to take some shortcuts. But, these workloads seem really small, IMO both languages should be returning pretty fast besides the startup overhead.

--- Edit ---

I saw comments about using bufio to buffer my writes. And holy moly.

[kamori@kamori-pc playground]$ cat bufferio.go 
package main

//import "fmt"
import "os"
import "bufio"
func main(){
   os.Stderr.WriteString("Printing Hello World! 1000000000 times")
   mywrite := bufio.NewWriter(os.Stdout)
   for i := 0; i < 1000000000; i++{
       mywrite.WriteString("Hello World!\n")
   }
}
[kamori@kamori-pc playground]$ go build bufferio.go 
[kamori@kamori-pc playground]$ time ./bufferio >/dev/null
Printing Hello World! 1000000000 times
real    0m9.882s
user    0m9.003s
sys 0m0.865s

--- Edit2 --- For dave's answer I went ahead and set flush=True and here are the results. This is what I expected to see in my original comparison. Looks like buffering saves the day!

[kamori@kamori-pc playground]$ cat print_with_flush_true.py 

for i in range(1000000000):
    print("Hello World!", flush=True)
[kamori@kamori-pc playground]$ time python3 print_with_flush_true.py >/dev/null 

real    27m38.319s
user    23m15.274s
sys 4m16.869s

Upvotes: 2

Views: 426

Answers (1)

dave
dave

Reputation: 64727

Python3 buffers the output of print.

https://docs.python.org/3/library/functions.html#print

Whether output is buffered is usually determined by file, but if the flush keyword argument is true, the stream is forcibly flushed.

You would need to do:

print("Hello World!", flush=True)

for the tests to be equivalent

Upvotes: 6

Related Questions