Why is Java faster than Python on this example loops?
Novice Explanation: Think of a program like a freight train that lays its own train-track as it moves forward. Track must be laid before the train can move. The Java Freight train can send thousands of track-layers ahead of the train, all working in parallel laying track many miles in advance, wheras python can only send one laboror at a time, and can only lay track 10 feet in front of where the train is.
Java has strong types and that affords the compiler to use JIT features: (https://en.wikipedia.org/wiki/Just-in-time_compilation) which enable the CPU to fetch memory and execute instructions in the future in parallel, before the instruction is needed. Java can ‘sort of’ run the instructions in your for loop in parallel with itself. Python has no concrete types and so the nature of the work to be done has to be decided at every instruction. This causes your entire computer to stop and wait for all the memory in all of your variables to be re-scanned. Meaning loops in python are polynomial O(n^2)
time, wheras Java loops can be, and often are linear time O(n), due to strong types.
I think I am making mistakes because I know Python is used by lots of scientific projects.
They’re heavily using SciPy (NumPy being the most prominent component, but I’ve heard the ecosystem that developed around NumPy’s API is even more important) which vastly speeds up all kinds operations these projects need. There’s what you are doing wrong: You aren’t writing your critical code in C. Python is great for developing in general, but well-placed extension modules are a vital optimization in its own right (at least when you’re crunching numbers). Python is a really crappy language to implement tight inner loops in.
The default (and for the time being most popular and widely-supported) implementation is a simple bytecode interpreter. Even the simplest operations, like an integer division, can take hundreds of CPU cycles, multiple memory accesses (type checks being a popular example), several C function calls, etc. instead of a few (or even single, in the case of integer division) instruction. Moreover, the language is designed with many abstractions which add overhead. Your loop allocates 9999 objects on the heap if you use xrange – far more if you use range
(99999999 integer minus around 256256 for small integers which are cached). Also, the xrange
version calls a method on each iteration to advance – the range
version would too if iteration over sequences hadn’t been optimized specifically. It still takes a whole bytecode dispatch though, which is itself vastly complex (compared to an integer division, of course).
It would be interesting to see what a JIT (I’d recommend PyPy over Psyco, the latter isn’t actively developed anymore and very limited in scope anyway – it might work well for this simple example though). After a tiny fraction of iterations, it should produce a nigh-optimal machine code loop augmented with a few guards – simple integer comparisions, jumping if they fail – to maintain correctness in case you got a string in that list. Java can do the same thing, only sooner (it doesn’t have to trace first) and with fewer guards (at least if you use int
s). That’s why it’s so much faster.