Which is faster, Python or Go? And by how much? This is the question I found myself asking earlier this week after troubleshooting a script that my son had written in Python to calculate prime numbers.
In The Red Corner – Python
My son worked out a fairly simple algorithm to generate prime numbers which we tweaked slightly to optimize it (things like not bothering to check even numbers, not checking divisors that are larger than 1/3 of the number, not checking any number ending in 5, and so on). I’m not saying that this is production-ready code, nor highly optimized, but it does appear to work, which is what matters. The resulting code looks like this:
#!/usr/bin/python max = 100000 for tens in xrange(0,max,10): for ones in (1, 3, 7, 9): a = tens + ones halfmax = int(a/3) + 1 prime = True for divider in xrange (3, halfmax, 2): if a % divider == 0: # Note that it's not a prime # and break out of the testing loop prime = False break # Check if prime is true if prime == True: print(a) # Fiddle to print 2 as prime if a == 1: print(2)
Yep, I really still need closing braces to see what’s going on. Anyway, the idea was to print out prime numbers between 1 and 100000. So let’s time it running:
$ time python prime.py [...] python prime.py 6.58s user 0.08s system 98% cpu 6.764 total
Not bad; python generated 9592 primes in ~6.8 seconds.
In the Blue Corner – Go
Being a smarty pants I thought I’d use this as an excuse to practice writing in go. I ended up fiddling the values To be as fair as possible, I used the same algorithm to calculate the primes and ended up with code looking like this:
package main import "fmt" func main() { max := 100000 for tens := 0; tens < max; tens += 10 { for _, unit := range []int{1, 3, 7, 9} { a := tens + unit // Loop through possible divisors maxhalf := int(a/3) + 1 prime := true for divisor := 3; divisor < maxhalf; divisor += 2 { if a%divisor == 0 { prime = false break } } if prime { fmt.Printf("%d\n", a) } if a == 1 { fmt.Printf("2\n") } } } }
That should be pretty simple to follow, I hope. This code also appears to work, so I timed this as well.
And We Have A Winner!
How did Go perform? As a reminder, Python took ~6.8 seconds to generate the 9592 primes between 1 and 100,000. Here's go compiling on the fly:
$ time go run prime.go [...] go run prime.go 0.93s user 0.08s system 103% cpu 0.967 total
Get that; Go performed the same task in just under 1 second. Now I need to know what happens when I actually compile the go code and run the resulting executable.
$ go build prime.go $ time ./prime [...] ./prime 0.66s user 0.02s system 97% cpu 0.688 total
Your eyes are not deceiving you. Go produced the same 9592 numbers in 0.69 seconds, almost ten times faster than Python. That is just something else altogether.
My 2 Bits
It may be that I'm doing something in Python that's an obvious don't do
or that I'm simply using a very computationally expensive way to achieve my goal, and any experienced Python programmer would suggest I did it a different way. However, for something so simple to see an order of magnitude difference between the two languages either makes Python look very slow, or Go look very fast, or maybe a bit of each, at least for this particular calculation.
I will say that Go has been impressing me with how quickly it performs REST API interactions in some scripts I've been writing, but even so, wow.s
So why is Python apparently so slow here? Can you optimize either piece of code and either narrow or widen the results gap on your system?
This is most likely because Python is really an interpreted language (it compiles to byte code which is interpreted) and Go is natively compiled (as I understand it). My guess is if you found a native python compiler you would see similar results as Go, especially on such a trivial program.
I agree. That said, part of the point was the observation that so much of what we hear about these days is in Python, and that’s interesting because performance wise, maybe it’s not so great.
Could be dating myself, but back when I studied CompSci compiled was always faster, usually very much so. As hardware has gotten better, the performance advantages of compiling vs. interpreted faded somewhat (interpreted languages were for learning only…nothing commercial was ever released and now it’s common). So it is somewhat surprising to see 10X difference.
For what it’s worth, Perl is even worse in my testing:
“Compiled” (hah!) using pp:
Versus (since I didn’t redirect to /dev/null in the article itself):
Python:
Go (compiled on the fly):
Go (compiled):
Agreed about the 10X difference. Where I’m going with this is that while I write a lot of perl, and I dabble with Python too, if this is even vaguely representative of the difference between the interpreted performance of python versus the compiled performance go, then it’s a very compelling case to consider writing in an arguably more difficult compilable language. I’m sure that C would be similarly fast, but it may be significantly harder when it comes to networking commands compared to Go which has a bunch of built-in functions and add-on modules to do the things I need to do.
Statistically, I can’t draw any conclusions here, of course, but it’s intended to act as food for thought. I have definitely noticed that my Golang REST client is blindingly fast compared to those I’ve written in Perl. That may be my bad programming, or a bad module choice, but it’s true for me at least.