Is Python That Slow?
blog.miguelgrinberg.com
blog.miguelgrinberg.com
Would an equivalent amount of effort on Python have ended in similar performance? Or do python things like "everything's an object", etc, set a hard ceiling?
* microbenchmarks comparing speed of doing stupid things (like adding 10000 integers or sorting a list with bubblesort 1000 times)
* 'real world'-ish benchmarks comparing idiomatic solutions in two languages doing the same thing
In both cases it doesn't matter (much) how big a standard library is. If you want to compare two languages doing something complex, you need to have -standard or not- implementation of that something for both languages.
But maybe I (and possibly others) have missed your point?
Such benchmarks don't compare anything in a meaningful way, and, in the second case, don't even compare what they claim to compare (you don't compare the languages if you run some third-party code on top of it, which has nothing to do with how the language itself is implemented).
These "benchmarks" just score some holly-war points for people who either irrationally like, or irrationally hate a particular language...
I kind of disagree on the meaningfulness of microbenchmarks, they give a feel for the performance, even if it's not a perfectly useful apples-to-apples comparison.
Like, if decoding a large JSON takes 3 milliseconds in one language and 2 minutes in another, that's signaling that the second language is a worse fit for certain projects. Even if the benchmark isn't super rigorous.
In order to establish that some language is faster than the other, you can devise a convincing metric. It will have to state what are the aspects that you are going to take into account, what are you baseline assumptions about measuring speed. Those using your experiment then will be able to practically use your results because they will be able to interpret your meaning of speed of the language.
The test in OP doesn't give anything like that. It ignores a lot of common testing practices by failing to control for many obvious confounds, by failing to provide any kind of sensible explanation of what is meant by the speed of the language, by using unreliable measuring techniques. It's just a hands-down awful way to test anything by the standards of people in the field of performance testing.
To further illustrate my point. Suppose you trust OP when they say that Python and JavaScript are roughly similar when it comes to the speed of calculating Fibonacci numbers. You then take a 16-cores server and run 16 processes of Python program calculating Fibonacci numbers and in another instance you take the same server and run 16 JavaScript process doing the same... only to discover that, eg. JavaScript now does 16 times better than Python (because Python decided to run all programs on the same core).
Note, I'm not saying that that's how Python actually works. All I'm saying is that the author doesn't control for this very obvious feature of contemporary hardware and never explicitly acknowledges any baseline assumptions about how the program is supposed to be executed. This is bad testing practice, intentionally or not it can be used to score "political" points, to promote a particular program over another.
What if "the language itself" provides arbitrary precision arithmetic by wrapping GMP and GMP is not written in "the language itself" ?
If the language's document states that the language has arbitrary precision operations, then the authors of the language are free to implement it however way they want, let it be monkeys with abacus, it's still part of the language.
We can only guess, but I believe it's because even though that is true, that python packs a lot more in its library than js, therefore more batteries, more time spent including those batteries and less time spent optimizing the interpreter, but standard benchmarks do away with all of that and focus on simple problems, like adding two numbers, to see how each language behaves.
Broadly speaking, PyPy is to Python as v8 is to JavaScript.
So I don't think it's attributable to Google's implementation.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
how can it be possible that nodejs is so fast, or that rust isn't multiple orders of magnitude faster (again, for the OPs benchmark it's about 8-10x faster in one case, and only twice as faster in another, which is much faster, but I expected it to be more like 100x faster).
is v8 really that fast? I'd be curious to see a multi threaded version of this implemented (some arbitrary multi-threaded benchmark). there I would hope that rust is multiple orders of magnitude faster, given that node is single threaded (with some caveats, of course as anyone who knows node knows). surely the rust written wasn't written optimally.
given how easy it is to write typescript compared to Rust and Python imho, maybe the JS community isn't as crazy as I thought
A downside is that you don't have as much time as time spent compiling is not spent actually doing the work. And I think typically for these dynamic languages, you still need some guard code at the edges to make sure the types you see are consistent with the compiled portion.
But there's the upside that you can observe runtime behaviour which is much more difficult to get into an ahead-of-time compiler. For instance, you might be able to JIT into a regexp engine executing a particular regexp.
Speed and easiness of writing aren’t the only two metrics that matter. Unless you subscribe to “move fast and break things”, I guess. Simplicity of packaging/distribution/embedding, artifact size, reliability and trustworthiness of the ecosystem, pre-distribution correctness checks… All of those are important to consider.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/
Some charts
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Some numbers -- Node.js versus Rust
(the underlines show links to program source code)
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
The article takes a tiny slice of Python: two exceptionally simplistic algorithms that aren't representative of any sort of real-world workload an actual Python program might be tasked with, and then adds a bunch of unknowns s.a. how long does it take the system to load the text of the program, anything about the load of the system running the test etc.
I feel like this is another one of those "politically" motivated articles (i.e. the author likes Python and wants to say something nice about it, regardless of the actual state of events in Python). But then also tries to serve this opinion as "well, this is my take, yours could be different, nobody knows who's right, but nobody else rose to the task of figuring it out, so you might as well go with what I say", as in:
> In the title of this article I asked if Python is really that slow. There isn't an objective answer to this question, so you will need to extract your own conclusions based on the data I presented here, or if interested, run your own benchmarks and complement my results with your own.
Well, no, there is an objective way, but it's hard! It's a lot of work to figure out what's the minimum required time is for the task, or the minimum space etc. and then see if your program, that was proved to be optimal for the task in the given language approaches that limit, and what are the contributing circumstances.
Just adding @numba.njit above the factorial function gives me a 36X speedup, putting it ahead of PyPy/Node.js and just a couple of times slower than Rust.
time python fibo.py 999
999 26863810024485359386146727202142923967616609318986952340123175997617981700247881689338369654483356564191827856161443356312976673642210350324634850410377680367334151172899169723197082763985615764450078474174626
python fibo.py 999 0.04s user 0.03s system 39% cpu 0.180 totalthank you
what would you prefer using? how do you compare python loops and those of that other language?
Of course, people already had.
Of course, NIH, so instead of references to those more comprehensive benchmarks it's recursive fib again.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Is PyPy used a lot? Why don't we hear more about it? Does it support the same large ecosystem of packages? And does it have a GIL?
pypy is used in some interesting places; at some point development was sponsored by a sports betting arbitrage company; you can research a bit more starting from https://pypy.org/pypy-sponsors.html and reading the links from https://pypy.org/categories/sponsors.html
take a look at: https://doc.pypy.org/en/latest/cpython_differences.html#c-ap...
As pointless benchmarks seem to be the thing, here's the numbers for different flavours of the one from the article with "10 20 30 40 50" as the args to make it a teeny bit more realistic:
C 27sec
Java 39sec 1.4x C
GraalPy 139sec 5.1x C
PyPy 252sec 9.3x C
CPython 1780sec 66.3x C
Not that this will tell you much about real-world use cases, of course, but hell, it's Friday afternoon...
Everyone said python was slow, learn C++. So i did. I never taught myself python until much later. Huge regrets. All of my open source projects are now in python.
I've discovered there's literally only 1 thing in python that causes it to be slow.
Global Interpreter Lock.
Use multiprocessing and avoid this problem? Now python is as fast as I ever need it to be.
I follow the ubuntu server releases on my projects. Ubuntu 24 is rocking 3.12 right now.
I am so excited to try 3.13.
Have you made the move over?
That’s as much as I can say as an anecdote.
Edit: Cython not CPython
All else bring equal highly flexible languages where the code continuously has to check the current type of the value of a variable and/or functions can be updated/replaced/etc tend to be slower than more strongly typed ones in both interpreters and compilers.
If you're doing anything even mildly related to ML/stat, Python is essential. It's close to impossible to replicate the eco-system that has been built out in the past 10 years (the Torch people were still in Lua back then).
I never dove into it but when I spent two days at work in 2021 on a hackathon to make “something anything” with “AI” it was very hard to just google and figure things out (with no prior knowledge and no time).
We did not end up using pytorch because the setup of a python toolchain that just works was very daunting and all the blogs and guides did not explain how to connect any of the pieces that make up training or a model or inference.
We landed on TensorFlow with golang and completed the hackathon with a working game. Granted, we probably used five lines of python for the training and defining the network. It seems to me that Python in that case did not bring much into the picture, except the five lines we cargo culted to define the network because we did not have time to find any sort of documentation for beginners to just build something (in the time we had).
I think a hackathon will inevitably favor quick copy-pasting, gluing APIs together, and utilizing tools you're familiar with - as opposed to being a good place for beginners to build an understanding of the fundamentals.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
It's amazing what he can do when not operating at full strength.
Huh? I mean, author's own benchmarks prove that Python is, indeed, that slow. Not everyone is going to be running their python code through PyPy. There are areas in which Python is invaluable and irreplaceable, but, let's accept things for what they are - it's slow. It only keeps up through libraries that leverages C code, and for those things - it is very acceptable.
And:
"Python is by far the fastest to write, cleanest, more maintainable programming language I know ... I rarely feel Python slows me down, and on the other side I constantly marvel at how fast I code with it compared to other languages."
In other words, "I don't know much about anything else other than Python."
What a pointless article.
The low barrier for entry is what they have in common. I remember that as a beginner everything outside my first language was a bit scary, so for some it is an emotional matter.
Trying to bolt on typing or making it a bit less "wasteful" is (although a fun exercise or probably necessary for some that have invested too much into it) ultimately a sign that you should move on to (dare I say) more apt ecosystems.
That is ok, part of growing as a developer. Just take with you what you have learned.
Python is by far the fastest to write, cleanest, more maintainable programming language I know, and that a bit of a runtime performance penalty is a small price to pay when I'm rewarded with significant productivity gains. I rarely feel Python slows me down, and on the other side I constantly marvel at how fast I code with it compared to other languages.
Personally I prefer Raku [https://raku.org] to do the same.
Here is the Python code from the article:
import sys
def fibo(n):
if n <= 1:
return n
else:
return fibo(n-1) + fibo(n-2)
if __name__ == '__main__':
for n in sys.argv[1:]:
print(n, fibo(int(n)))
And here is my Raku code: sub MAIN(*@n) {
say (0, 1, *+* ... *)[@n]
}
So, even better on the "quick to code and easy to maintain" imo.The performance comparison is interesting too...
CPython 9.72 - 22.10 secs
PyPy 1.65 secs
Node 1.76 secs
Rust 0.25 secs
Raku 0.12 secs
> time ./fibo.raku 10 20 30 40
(55 6765 832040 102334155)
./fibo.raku 10 20 30 40 0.12s user 0.02s system 114% cpu 0.124 total
Haha (admittedly I am on an M1 not an i5 so this is a bit Apples vs. Oranges ... but CPUs getting faster all the time and Raku has no GIL)When I say "haha" I mean <<that's quite funny since by chance the code given in the OP is much faster in Raku _in this particular instance_ and that's quite apt since some higher level operators such as `...` are actually quite useful and since they are core operations can be more tightly optimised than general code. So actually (in addition to the main thrust that human speed is more important than code speed), it seems that often our preconceptions are not always true>>
I am genuinely sorry that you find the raku gibberish - I suppose that I would find Malay gibberish but that's a reflection on me not on the inhabitants of Malaysia that use it every day.
Let me try a translation:
The sequence operator is spelled `...`. Here are a couple of basic examples:
say 1,2,3 ... 10; #(1,2,3,4,5,6,7,8,9,10) - an arithmetic sequence
say 1,2,4 ... 16; #(1,2,4,8,16) - a geometric sequence
say 1,2,4 ... *; #(1,2,4,8,16,32...) - lazy list with infinite length
The RHS value limits the size of the last item in the output list. A `` on the right means `infinity`.You can see that this looks at the list of three items on the LHS and determines the remainder of the sequence from that. If you want to use it with a function, you can use the `` to represent a parameter like this:
say (1, *+7 ... *)[1..4]; #((8 15 22 29)
You will note that I am using the regular `[]` index syntax with the range `1..4` to request only the first 4 items of the resulting lazy list.So, the final bit of the puzzle is when I have a dyadic function that takes two parameters (ie the previous value and the one before it), like this:
say (0,1, *+* ... *)[10,20,30,40];
This is the fibonacci series.---
One last word is that the raku MAIN() function is a very neat way to deploy a raku script on the command line.
sub MAIN(*@n) {
say "$_ " ~ (0, 1, *+* ... *)[$_] for @n
}
So that's "slurp" all the indices provided on the command line to array `@n`, which can then be used as the index to the sequence.PS. I have tweaked this a bit to include printing the index and the result. ---
Even if this helps to de-gibberish the code, I understand that you may not like it. Personally I find it quite clean and easier to read than the Python code.
You missed a word. I've guessed it was the word "guess". :)
I haven't checked Rakudo's code but I'm pretty sure any performance optimizations of your code were not related to using hyperoperators or internal concurrency.
Here's two things I can think of that may be relevant:
* Rakudo tries to inline (calls to) small routines such as `* + *`. Wikipedia's page on inlining claims that, when a compiler for a low level language (like Rust) succeeds in inlining code written in that language it tends to speed the code up something like ten percent or a few tens of percent. In a high level language (like Raku) it can result in something like a doubling or, in extreme cases, a ten fold speed up. The difference is precisely because low level languages tend to compile to fast code anyway. So while this may explain why Raku(do) is faster than CPython, it can't explain your conclusion that Rust is half as fast as Raku. (I think you almost certainly made a mistake, but let's move on!)
* In your Raku code you've used `...`. That means all but the highest number on the command line are computed for free, because sequences in Raku default to lazy processing, and lazy processing defaults to use of caching of already generated sequence values. So a single run passed `10 20 30 40` on the command line would call the `* + *` lambda just 40 times instead of 100 (10+20+30+40) times. That's roughly a doubling of speed right there.
So if Rakudo is doing a really good job of codegen for the fibonacci code, and you removed the startup overhead from your Raku timings, then perhaps, maybe, Raku(do) really is "beating" Rust because of the `...` caching effect.
I still find that very hard to believe but it would certainly be worth trying to have someone reasonably expert at benchmarking trying to repeat and confirm your (remarkable!) result.
> At this point in its evolution, there is still much work to be done on code optimisation.
Understatement!
It took over a decade for production JVMs and JS engines to stop being called slow, another decade to start being called fast (but not as fast as C), and another decade to be considered surprisingly fast.
Rakudo's first production release came less than a decade ago. So I think that, for now, a reasonable near term performance goal (I'd say "by the end of this decade") is to arrive at the point where people stop calling Raku slow (except in comparison to C).
> Let me try a translation:
Let me have a go too. :) But I'll rewrite the code:
sub MAIN #= Print fibonacci values.
(*@argv); #= eg 10 20 prints 55 6765
print .[ @argv ] # print fibonacci values.
given
0, 1, 1, 2, 3, 5, # First fibonacci
values.
sub fib ( $m, $n ) # Fibonacci generator
{ $m + $n } ... Inf # to compute the rest.**well - try it on your own machine - code as noted above (with reflection of the input value)
then,
> time raku fibo.raku 10 20 30 40
10 55
20 6765
30 832040
40 102334155
raku fibo.raku 10 20 30 40 0.11s user 0.01s system 116% cpu 0.133 total
Caveat - this is absolutely not purporting to be a benchmark, it was as big a (pleasant and counterintuitive) surprise to me as to anyone.Hi librasteve. :)
FWIW I find it really hard to believe your bench marking was reliable / repeatable. Mistakes happen, and I think mistakes must have happened in this case.
I'll write more about this in another reply to another of your comments.
> (…)
> Before you ask, no, I did not include Ruby, PHP or any other of the "slow" languages in this benchmark, simply because I don't currently use any of these languages and I wouldn't start using them even if they showed great results on a benchmark.
Why bother with benchmarks, then? Clearly you don’t care for them and just want to continue using Python because that’s what you’re familiar with and like. That’s fine, it’s your prerogative, you don’t have to justify that choice to anyone who doesn’t have the same priorities you do.
Personally I dislike Python and find it to be far from “the fastest to write, cleanest, more maintainable programming language” and I wouldn’t want to use it even if its performance were amazing. But that doesn’t matter, if we all liked the same things we wouldn’t have so many programming languages. Or operating systems. Or brands of sneakers. Or…
When I run benchmarks, I do so in real use cases, to test different algorithms for a task that will be in production.
I’ve lost track of how many times someone jumped to “ZOMG THE GIL SUCKS!!!!” but the real lesson turned out to be something like “maybe I shouldn’t open files in the middle of a nested loop”[1] or “the C extension used for dict/re/Numpy/etc. is actually pretty hard to beat”. One big problem for comparisons is that most of the time people don’t do a straight rewrite but change the algorithm based on the experience they got writing the first version - my Python beating C examples all fell into that category where it was really a refactor nobody wanted to do in a gnarled C codebase, or simply didn’t have time to make in a more limited language.
1. Someone I know ranted about Python, spent a month rage-porting to Go, and saw ~10% over the improved Python version.
We can explore the alternative and transliterate in a very literal way:
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Casey Muratori has a real in-depth course on why Python is slow and how to make it fast, among many other things why software can be slow.
I don’t think that’s a fair characterisation of the course and that description does it a disservice. The course is not about Python, it’s about programming with performance in mind.
The video on Philosophies of Optimisation is a good primer on the tenets of the course.