24 ms·
Why Python Is Slow: Looking Under the Hood
- shanemhansen 12y agocPython does alot of work for just about any piece of code. Just a small handful of things: 1. everything's a hash (object data, variable lookup, etc). jmoiron wrote a great article on the topic http://jmoiron.net/blog/whats-going-on/ http://jmoiron.net/blog/whats-going-on/ 2. refcounting introduces overhead for every variable access 3. loops are making all kinds of function calls to __iter__, next(), and catching StopIteration, which makes it hard to have a tight loop. 4. There's the GIL, builtin locking isn't great. 5. lots of indirection (lack of value types)
- richardwhiuk 12y agoPreviously: https://news.ycombinator.com/item?id=7721096 https://news.ycombinator.com/item?id=7721096 This is at least 6 months old.
- veneratio 12y agoAh I thought I had seen this before.
- btown 12y agoWhy, oh, why does CPython bother keeping refcounts for small integers? Sure, it lets you make pretty graphs with [sys.getrefcount(i) for i in range(1000)]... but that's an extra memory read and write on every instruction that uses an integer. I can only imagine that not only are these extra instructions, but they're extra instructions that kill pipelining, if the interpreter needed to do "a = 1; b = 1; c = 1" for instance. Maybe they found that the branch needed to not bother refcounting for small integers was even slower? Does anyone know if that was ever tried?
- CyberDildonics 12y agoDoes that also imply that a small integer is created on the heap? I would think the heap allocation would be the huge performance killer.
- plikan13 12y agoPython is not going to call malloc(..) for every single object that you create. It will allocate a block of same-sized objects and maintain a linked list of free slots.
- dalke 12y agoWhat you propose sounds like it would be a pure headache for all code which otherwise expects a uniform memory API. Consider a C extension which takes an object and appends it to a list. If small integers did not have a refcount then that extension would have to have special code, like "if object is not a small integer, then increment the reference count".
- btown 12y agoI hadn't considered that extensions which needed direct memory access would need access to these objects. But it's a solvable problem. The proposal might read as follows: "Objects with IDs between 0 and 2055, inclusive, are considered unreleasable. The Python runtime shall initialize the refcount of these objects to 1 when they are first used, and shall make no guarantee that the refcount on such objects accurately reflects the true number of references to that object. Native extensions may increase and decrease the refcount on these objects as if they were normal objects; if those extensions are well-behaved, then the refcount should never decrease below 1. However, native extensions that can statically reason about whether an object is likely to be unreleasable, may benefit in performance by checking for whether objects passed to their functions are indeed unreleasable, and refraining from modifying or checking their refcount in such cases."
- ggchappell 12y ago> 2. Python is interpreted rather than compiled. Can we stop saying things like this? Virtually all Python is compiled, as part of the interpretation process. There is a valid point here, of course. The point is that the top Python compilers do not compile to native code. So say that. Words have meanings. Use them correctly. <grumble, grumble>
- bunderbunder 12y agoMight want to start by asking the core Python community. Check out the first sentence on wiki.python.org. Nowadays it's the norm for interpreted languages to be JIT-compiled. They still universally call themselves interpreted languages because the jitter is an implementation detail, and the use of one does nothing to negate the characteristics they're trying to advertise when describing themselves as interpreted languages. Think of it this way: Can I take a text file containing source code in the language and execute it anywhere that has a special program for executing files written in that language (commonly known as an interpreter)? Or do I have to manually perform an intermediate step of converting the program to some binary representation using a special program for doing that (commonly known as a compiler), and then execute the output that program produces?
- lispm 12y agoThere are several compiler-only Lisp implementations which can execute source from text. Nobody there would call them interpreter because of that. In Lisp we call them interpreter, when the implementation traverses the source code during execution. If it compiles the source to some byte-code or machine code, we call in Compiler. The incremental nature of a compiler, then does not make it that we would call it an Interpreter. We would call it an incremental compiler. That's a compiler, which can compile all expression, regardless of the size of the expression, not just 'whole programs'.
- ggchappell 12y ago> There are several compiler-only Lisp implementations which can execute source from text. Why not? I would.
- slashnull 12y agoShort answer: because it doesn't JIT
- ludamad 12y agoThat's a lossy short answer. LuaJIT with JIT turned off still runs circles around CPython.
- trynumber9 12y agoEven normal Lua is fast compared to Python, if the shootout games are any bit accurate. http://benchmarksgame.alioth.debian.org/u64/benchmark.php?test=all&lang=lua&lang2=python3&data=u64 http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...
- igouy 12y agoYou are told how the measurements were made -- Are the benchmarks game measurements accurate? I think these questions are more to the point: Were the Python programmers as skilled as the Lua programmers? Do the programs do anything like the things your programs do?
- slashnull 12y agoTrue. So, with element of answer 1) ruled out as Lua is also dynamic, and 2) ruled out by your evidence, remains 3. Python's object model can lead to inefficient memory access
- philippeback 12y agoPharo 3's results (JIT enabled VM) [(1 to: 100000000) sum] timeToRun 0:00:00:07.335 http://pharo.org http://pharo.org Version 4 with new VM due in 2015. Will perform much much better as with the range used, we need to use LargeIntegers as the 32 bit VM must promote to LargeInteger objects. With the 64bit VM, all fits in.
- masklinn 12y ago> pypy -mtimeit 'sum(xrange(1, 100000001))' 10 loops, best of 3: 153 msec per loop however the context of TFA is scientific computing, hence pypy being ignored/dismissed
- philippeback 12y agoWe can do it this was if needed. http://clementbera.wordpress.com/2013/06/19/optimizing-pharo-to-c-speed-with-nativeboost-ffi/ http://clementbera.wordpress.com/2013/06/19/optimizing-pharo...
- masklinn 12y agoHaving an FFI is not relevant, Pypy has an excellent FFI already[0]. You can't interface with the existing SciPy ecosystem which expects the CPython API, which is the reason why pypy doesn't matter in scientific computing. [0] inspired by LuaJIT and usable from both CPython and PyPy: https://cffi.readthedocs.org/en/release-0.8/ https://cffi.readthedocs.org/en/release-0.8/
- deleted 12y ago[deleted]
- Skunkleton 12y agoAnother reason python is slower than other languages is that abstraction adds significant overhead which most implementations cannot optimize away. See here: http://blog.reverberate.org/2014/10/the-overhead-of-abstraction-in-cc-vs.html http://blog.reverberate.org/2014/10/the-overhead-of-abstract...
- coolsunglasses 12y agoThe overhead of abstraction...unless you're using Haskell where typeclass instances get specialized, so you can abstract without slowing things down.
- haberman 12y agoThe article is not suggesting that abstraction inherently has overhead in all languages. Rather it is exploring the difference between languages where abstractions can be essentially free (in terms of runtime overhead), and languages/implementations where abstraction has a noticeable cost.
- tome 12y agoLet's not overstate the case. There's still plenty of abstraction which is tricky or impossible for GHC to optimize. But yes, with static analysis comes the possibility of nice optimizations.
- coolsunglasses 12y agoIndeed. I like the tricky one they're kicking around that would make `lens` faster. Pretty intriguing. I'm hopeful they'll be able to make it work in STG.
- pnathan 12y agoHeh, Python is slow: in order to write fast Python, you have to write in C and conform to the FFI. I vastly prefer to use tools which don't come misshapen out of the box, personally.
- the_real_bto 12y agoDifferent tools for different jobs. Being able to develop code quickly is a huge win that Python delivers. Sometimes that makes Python the right tool, e.g. Youtube. Other times slow execution speed makes it the wrong choice. [0] http://www.gooli.org/blog/youtube-runs-on-python/ http://www.gooli.org/blog/youtube-runs-on-python/
- pnathan 12y agoother tools also deliver code quickly. that's an evasion of the criticism.
- tjradcliffe 12y agoA related question is: why is Perl so fast? Quite a few years ago I wrote a little Runge-Kutta solver in Perl for some simulation work. It seemed like a good idea at the time. The equations of motion had to be integrated over a very long time, and it could take hours for a single run (still much faster than the Monte Carlo it was being used to do a sanity-check on). I re-wrote everything in C++, and picked up less than a factor of two in speed. So it isn't that "Python is interpreted" that is the problem, because "Perl is interpreted" in exactly the same way. It really does seem to come down to the Python object model. Perl's scalar types have vastly less overhead, so much so that you can actually do reasonably efficient numerical computation in it. I abandoned Perl for Python shortly thereafter because once I got over the "oh my god it's full of whitespace" thing Python was just more fun to code in, but the speed that Perl provided is something I've definitely missed, and it was a real awakening to the notion that interpreted languages don't have to be slow. The striking thing was that unlike Java (say) where it can be fast but you generally have to think about it, I was getting fast Perl without even really trying.
- tenfingers 12y agoAlso had the same experiences. Perl is relatively fast and memory compact compared to other modern scripting languages. Perl also has PDL (http://pdl.perl.org/ http://pdl.perl.org/), which is very similar in scope to numpy, but predates it by several years.
- dagw 12y agopredates it by several years If you want to get technical, Numpy is a continuation of Numeric which in turn pre-dates pdl.
- haberman 12y agoI'm not sure your results are typical. In microbenchmarks, Perl is 2-125x slower than C++: http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=perl&lang2=gpp&data=u32 http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te... And Java is quite a bit faster than Perl too: http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=perl&lang2=java&data=u32 http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te... Perl isn't really that fast. It's faster than Python in most cases, but gets beat by Lua pretty consistently: http://benchmarksgame.alioth.debian.org/u32/benchmark.php?test=all&lang=perl&lang2=lua&data=u32 http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...
- hackersf 12y agoPython's GC is a conservative collector as opposed to Java's precise collector => terrible GC compaction.
- hackersf 12y agoPython's GC is a conservative collector as opposed to Java's precise collector => terrible GC compaction.
- TheLoneWolfling 12y agoIs it just me who sees a large chunk of these as flaws in the Python implementation as opposed to the Python language? Dynamic typing can often be optimized at the compilation stage - and yes, Python has a compilation stage - this particular example is basic type inference, for example. Even more complex examples can be optimized by emitting specialized versions for the types that the compiler can see it will be called with. Effectively emitting specialized versions of specific nodes in the control flow graph.
- dagw 12y agoIs it just me who sees a large chunk of these as flaws in the Python implementation as opposed to the Python language? There are a number of design decision in python language that makes it inherently hard to write a fast python implementation.
- Animats 12y agoAh, the right answer! There are lots of languages where variables are dynamically typed that can be compiled and optimized with a JIT compiler. In Python, though, any code can mess with any data, using "setattr". You can find all the variables in another module and mess with them at run time, using their names as strings. At compile time, the compiler can't detect that's going to happen. This is sometimes called the Guido von Rossum Memorial Boat Anchor. So Python implementations usually have to assume the worst case. Google's attempt at a faster, compatible Python, "Unladen Swallow", was an embarrassing failure. PyPy manages to get past that, but at a huge cost in JIT compiler complexity. After 12 years of work, PyPy is finally shipping stable versions and starting to get some use. It's been all uphill for the PyPy developers, though. It's kind of sad. Python never achieved its full potential because of the speed problem. Google ended up developing Go because Python was too slow.
- adamtj 12y agoHow is python substantially different from javascript in this case? Google has a crazy fast javascript JIT compiler, even though javascript has something equivalent to python's setattr().
- scott_s 12y agoEven if you're already familiar with all of the main reasons that answer the question (that is, you're already familiar with how a VM works), read the Just for fun: a few "never use these" hacks. I mean, wow. That's the kind of nastiness I normally associate with C's free-for-all memory model.
- slashnull 12y agoWith the nuance that C forces you to deal with those never use these hacks in order to do anything non-trivial. I can't see why anyone would go and mess with CPython's internals like that in production code. Other than for nefarious purposes, I guess.
- mahouse 12y agoI don't understand why is it compared to C, when it can be compared to Perl or PHP, which are also dynamically typed, interpreted, etc, but much faster.
- masklinn 12y ago1. Because TFA is about scientific computing 2. And in that context (and most others), the difference between Perl, PHP and CPython is basically non-existent, you might get 2x on one bench, half on the other
- slantedview 12y ago"it's slow" "just write a C extension" "but then why use Python?"
- nightcracker 12y agoBecause 90% of the runtime is spent in 10% of the code.
- bch 12y agoBecause beautiful control structures and a REPL ?
- pjmlp 12y agoThere are many languages that offer that alongside an AOT compiler to native code.
- bch 12y agoWell sure -- but I guess considering Python at all suggests you want or need to. Python control and the REPL are _fine_ reasons to use it. To be clear though, I'm a Tcl-er, but I use it in exactly this context. I meant my comment to stand along the other sibling comments.
- slantedview 12y agoI'm not sure that these are compelling reasons to use Python, given the alternatives. Squeezing performance out of Python often involves dropping down to C. This isn't the case with the competition.
- AlexeyBrin 12y ago"it's slow" "just write a C extension" "but then why use Python?" Because you typically need to write a C extension for like 2% or your code, while you could keep the remaining 98% in an easier to write (and more compact) language.
- deleted 12y ago[deleted]
- mikkom 12y agoAlmost all these points apply to javascript as well. Javascript, however, is not really slow anymore. http://benchmarksgame.alioth.debian.org/u64/benchmark.php?test=all&lang=v8&lang2=python3&data=u64 http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...
- jlebar 12y ago> Almost all these points apply to javascript as well. Sort of. The article talks about why CPython, an implementation of the Python language, is slow. You're right that a naive implementation of Javascript would have many of the same problems, and indeed many of the first implementations of JS did have these problems. But due to heated competition, Mozilla, Google, Apple, and Microsoft have invested a huge amount of top-quality engineer-years into optimizing their implementations of JS. Implementations of Python, in contrast -- and CPython in particular -- have not received the same sort of love. I largely agree with the notion that the speed of modern JS engines serves as an existence proof that Python could be similarly fast.
- anentropic 12y agothat's what PyPy is http://speed.pypy.org/ http://speed.pypy.org/
- jerf 12y ago"Javascript, however, is not really slow anymore." What's easy to lose track of under the steady stream of headlines about this microbenchmark going 5x faster and that microbenchmark going 10x faster and this one microbenchmark being "faster than C" is that Javascript is still a fairly slow language, though. Flick that to compare Javascript vs. C: http://benchmarksgame.alioth.debian.org/u64/benchmark.php?test=all&lang=v8&lang2=gcc&data=u64 http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te... It has come a long way. It is, of course, still usable for many things. But it is still an interpreted language, and my guess is that we've basically plateaued on performance for Javascript, and it's important to remember that it isn't as "fast as C" in any useful sense of the term. And for that matter, to get even that performance out of JS seems to require you to write a very specialized subset of JS that will tickle the optimizers correctly. In practice I suspect you will get even worse deltas over C, which, for all of its many failure modes, does make it often easier to do the fast thing than the slow thing, usually regardless of the correctness of either of them. (That's why we have asm.js, after all. You can't have both of "Javascript is as fast as C" and "asm.js is several times faster than Javascript", because the composition of both statements is absurd.)
- madengr 12y agoPlease forgive my ignorance, but why can't Python just be compiled into assembly like C? The compiler should be able sift through the code and see that "x=1" is an integer, or converts to a float when "x=x+1.0". If I'm doing something like counting from 1 to 10 and summing the count, why can't this be compiled to run the same speed as C? Obviously since it's interpreted, but when I'm done with development, why can't I just run it through a compiler to get fast assembly code? Way back when, you could get a BASIC compiler to turn your interpreted BASIC code into assembly. What fundamental am I missing?
- timClicks 12y agoI don't know the full story either, but I expect it's because lots of things change during runtime. It's too hard to lock things down.
- joe_the_user 12y agoThe problem is that an average interpret/compiler is going to be "dumb" in that it assumes that when it starts parsing a python file that all of the features are going to be used. It provides facilities for all of python abilities regardless of what the program is going to do. Now, there are subsets of python people have developed that can be compiled and all sorts of other stuff. Beyond that, I'd just say "all this stuff sounds easy but gets much harder when you actually have to program it". "Why can't it just know!" is the programmer's lament.
- kyllo 12y agoRuntime metaprogramming. In Python you can create new classes and add methods to existing classes at runtime. If you AOT-compile everything to assembly, you have to take away those runtime features, and then it's not Python anymore.
- vidarh 12y agoNo, you don't. You "just" need slower fallbacks that does metadata lookup for things that are not statically decidable. Consider how many people end up implement their own introspectable C object models.
- rurban 12y agoUnfortunately this blog post is misleading and uninformed. Python and the related other slow dynamic languages perl, php, and ruby are slow not because the dynamic type checks, branches and indirections are costly, but because they are badly written. They are just inefficient and poor interpreters, with bad ops and data structures. In comparison efficient dynamic languages like lisp, scheme, lua or javascript and many others not so well known do exist. Mine, potion, is also in the lua category, and not in the slow PPPR (python, perl, php, ruby) category. The seminal paper about efficient dynamic interpreters is Ertl's "The Structure and Performance of Efficient Interpreters" http://www.complang.tuwien.ac.at/papers/ertl%26gregg03jilp.ps.gz http://www.complang.tuwien.ac.at/papers/ertl%26gregg03jilp.p... The abstract is: "Interpreters designed for high general-purpose performance typically perform a large number of indirect branches (3.2%–13% of all executed instructions in our benchmarks). These branches consume more than half of the run-time in a number of configurations we simulated. We evaluate how accurate various existing and proposed branch prediction schemes are on a number of interpreters, how the mispredictions affect the performance of the interpreters and how two different interpreter implementation techniques perform with various branch predictors. We also suggest various ways in which hardware designers, C compiler writers, and interpreter writers can improve the performance of interpreters." My own input to this is that the GC, the JIT, the calling convention and esp. the size of the datastructures and ops do matter more, than eliminating the type checks.
- enupten 12y agoThe numerous Common Lisp implementations out there would agree with you.
- mamcx 12y agoExist a sample I can follow that is made in a imperative language? I'm fluent in python, pascal, C#, obj-c but can survive C if the code is clear...
- pjmlp 12y agoIt seems learning about compiler design is not as good as it used to be. Better learn about the JavaScript framework of the day.
- 12y ago
- rhgraysonii 12y agoOne of my favorite topics lately is Python optimization. A few weeks ago FB put out a release on how they had done fast randomized SVD and in the implementations section they mentinoned using intels kernel math libraries for the BLAS libraries. Largely the result of this is many more functions are F-Contiguous rather than C-contiguous (in linear algebra, fortan obviously wins here). I had done something similar on a much smaller scale about 4 months ago. https://medium.com/@_devbob/from-0-to-warp-speed-b780a2bc36ce https://medium.com/@_devbob/from-0-to-warp-speed-b780a2bc36c... Does anyone else have some good bits or blog entries on these sorts of inner workings and optimizations?
- sgt101 12y agoIn simple language -> because it's difficult to provide the machine with the information it needs to make decisions about efficiency.