Can Go really be that much faster than Python?

Question:

I think I may have implemented this incorrectly because the results do not make sense. I have a Go program that counts to 1000000000:

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 1000000000; i++ {}
    fmt.Println("Done") 
}

It finishes in less than a second. On the other hand I have a Python script:

x = 0
while x < 1000000000:
    x+=1
print 'Done'

It finishes in a few minutes.

Why is the Go version so much faster? Are they both counting up to 1000000000 or am I missing something?

Asked By: bab

||

Answers:

I’m not familiar with go, but I’d guess that go version ignores the loop since the body of the loop does nothing. On the other hand, in the python version, you are incrementing x in the body of the loop so it’s probably actually executing the loop.

Answered By: highlycaffeinated

This scenario will highly favor decent natively-compiled statically-typed languages. Natively compiled statically-typed languages are capable of emitting a very trivial loop of say, 4-6 CPU opcodes that utilizes simple check-condition for termination. This loop has effectively zero branch prediction misses and can be effectively thought of as performing an increment every CPU cycle (this isn’t entirely true, but..)

Python implementations have to do significantly more work, primarily due to the dynamic typing. Python must make several different calls (internal and external) just to add two ints together. In Python it must call __add__ (it is effectively i = i.__add__(1), but this syntax will only work in Python 3.x), which in turn has to check the type of the value passed (to make sure it is an int), then it adds the integer values (extracting them from both of the objects), and then the new integer value is wrapped up again in a new object. Finally it re-assigns the new object to the local variable. That’s significantly more work than a single opcode to increment, and doesn’t even address the loop itself – by comparison, the Go/native version is likely only incrementing a register by side-effect.

Java will fair much better in a trivial benchmark like this and will likely be fairly close to Go; the JIT and static-typing of the counter variable can ensure this (it uses a special integer add JVM instruction). Once again, Python has no such advantage. Now, there are some implementations like PyPy/RPython, which run a static-typing phase and should fare much better than CPython here ..

Answered By: user166390

It is possible that the compiler realized that you didn’t use the “i” variable after the loop, so it optimized the final code by removing the loop.

Even if you used it afterwards, the compiler is probably smart enough to substitute the loop with

i = 1000000000;

Hope this helps =)

You’ve got two things at work here. The first of which is that Go is compiled to machine code and run directly on the CPU while Python is compiled to bytecode run against a (particularly slow) VM.

The second, and more significant, thing impacting performance is that the semantics of the two programs are actually significantly different. The Go version makes a “box” called “x” that holds a number and increments that by 1 on each pass through the program. The Python version actually has to create a new “box” (int object) on each cycle (and, eventually, has to throw them away). We can demonstrate this by modifying your programs slightly:

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d %pn", i, &i)
    }
}

…and:

x = 0;
while x < 10:
    x += 1
    print x, id(x)

This is because Go, due to it’s C roots, takes a variable name to refer to a place, where Python takes variable names to refer to things. Since an integer is considered a unique, immutable entity in python, we must constantly make new ones. Python should be slower than Go but you’ve picked a worst-case scenario – in the Benchmarks Game, we see go being, on average, about 25x times faster (100x in the worst case).

You’ve probably read that, if your Python programs are too slow, you can speed them up by moving things into C. Fortunately, in this case, somebody’s already done this for you. If you rewrite your empty loop to use xrange() like so:

for x in xrange(1000000000):
    pass
print "Done."

…you’ll see it run about twice as fast. If you find loop counters to actually be a major bottleneck in your program, it might be time to investigate a new way of solving the problem.

Answered By: Sean McSomething

One billion is not a very big number. Any reasonably modern machine should be able to do this in a few seconds at most, if it’s able to do the work with native types. I verified this by writing an equivalent C program, reading the assembly to make sure that it actually was doing addition, and timing it (it completes in about 1.8 seconds on my machine).

Python, however, doesn’t have a concept of natively typed variables (or meaningful type annotations at all), so it has to do hundreds of times as much work in this case. In short, the answer to your headline question is “yes”. Go really can be that much faster than Python, even without any kind of compiler trickery like optimizing away a side-effect-free loop.

Answered By: hobbs

pypy actually does an impressive job of speeding up this loop

def main():
    x = 0
    while x < 1000000000:
        x+=1

if __name__ == "__main__":
    s=time.time()
    main()
    print time.time() - s

$ python count.py 
44.221405983
$ pypy count.py 
1.03511095047

~97% speedup!

Clarification for 3 people who didn’t “get it”. The Python language itself isn’t slow. The CPython implementation is a relatively straight forward way of running the code. Pypy is another implementation of the language that does many tricky (especiallt the JIT) things that can make enormous differences. Directly answering the question in the title – Go isn’t “that much” faster than Python, Go is that much faster than CPython.

Having said that, the code samples aren’t really doing the same thing. Python needs to instantiate 1000000000 of its int objects. Go is just incrementing one memory location.

Answered By: John La Rooy

@troq

I’m a little late to the party but I’d say the answer is yes and no. As @gnibbler pointed out, CPython is slower in the simple implementation but pypy is jit compiled for much faster code when you need it.

If you’re doing numeric processing with CPython most will do it with numpy resulting in fast operations on arrays and matrices. Recently I’ve been doing a lot with numba which allows you to add a simple wrapper to your code. For this one I just added @njit to a function incALot() which runs your code above.

On my machine CPython takes 61 seconds, but with the numba wrapper it takes 7.2 microseconds which will be similar to C and maybe faster than Go. Thats an 8 million times speedup.

So, in Python, if things with numbers seem a bit slow, there are tools to address it – and you still get Python’s programmer productivity and the REPL.

def incALot(y):
    x = 0
    while x < y:
        x += 1

@njit('i8(i8)')
def nbIncALot(y):
    x = 0
    while x < y:
        x += 1
    return x

size = 1000000000
start = time.time()
incALot(size)
t1 = time.time() - start
start = time.time()
x = nbIncALot(size)
t2 = time.time() - start
print('CPython3 takes %.3fs, Numba takes %.9fs' %(t1, t2))
print('Speedup is: %.1f' % (t1/t2))
print('Just Checking:', x)

CPython3 takes 58.958s, Numba takes 0.000007153s
Speedup is: 8242982.2
Just Checking: 1000000000
Answered By: John 9631

Problem is Python is interpreted, GO isn’t so there’s no real way to bench test speeds. Interpreted languages usually (not always have a vm component) that’s where the problem lies, any test you run is being run in interpreted bounds not actual runtime bounds. Go is slightly slower than C in terms of speed and that is mostly due to it using garbage collection instead of manual memory management. That said GO compared to Python is fast because its a compiled language, the only thing lacking in GO is bug testing I stand corrected if I’m wrong.

Answered By: Dawg
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.