Python versus Go – Fighting in Prime Time

Python vs Golang

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?

5 Comments on Python versus Go – Fighting in Prime Time

  1. 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:

          $  prime time perl prime.pl > /dev/null
          perl prime.pl > /dev/null  10.49s user 0.13s system 96% cpu 11.045 total
          

          “Compiled” (hah!) using pp:

          $ prime time ./perlcompiledprime > /dev/null
          ./a.out > /dev/null  9.31s user 0.05s system 99% cpu 9.380 total
          

          Versus (since I didn’t redirect to /dev/null in the article itself):

          Python:

          $ time python prime.py > /dev/null
          python prime.py > /dev/null  7.45s user 0.08s system 99% cpu 7.606 total
          

          Go (compiled on the fly):

          $prime time go run prime2.go > /dev/null
          go run prime2.go > /dev/null  0.99s user 0.14s system 82% cpu 1.368 total
          

          Go (compiled):

          $  prime time ./prime2 > /dev/null
          ./prime2 > /dev/null  0.71s user 0.01s system 99% cpu 0.721 total
          
        • 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.

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.