kmod's blog


Why is Python slow



The fundamental issue is:

Python spends almost all of its time in the C runtime








Update: why is the Python C runtime slow?


import itertools
sum(itertools.repeat(1.0, 100000000))











Filed under: Pyston Leave a comment
Comments (7) Trackbacks (0)
  1. If you really need performance, why not make use of Cython ( I may very well be missing the point here, but it seems like Cython already solves a lot of the performance issues surrounding Python by simply generating C code from the existing Python source, and then compiling it down to machine code.

  2. You say
    “Python opcodes are very complex,”
    Sure but that doesn’t necessarily mean that focusing on the C part will help a lot to improve performance.
    It might mean the root cause is that the opcode is very complex and less complex opcodes could help to get better JIT performance.
    The type check for example could maybe be inlined or avoided at all if the opcodes would to less work.

  3. As far as I understand your post, it’s just that the Python language does not allow optimizations.

  4. >Python is a much, much more dynamic language that even JS.
    Any example?

  5. I think I remember Brandon Rhodes showing in his talk “The Day of the Exe is Upon Us” that most of Cpython’s slowness came from its dynamic nature (i.e. the time it spends resolving the type of objects). In that respect yes Cython vastly improves the speed but at the expense “Dynamic”ism.

  6. Has anyone here tried PyPy? To quote the website: “PyPy is a replacement for CPython. It is built using the RPython language that was co-developed with it. The main reason to use it instead of CPython is speed: it runs generally faster” –

  7. Python performance is very much a bifurcated issue split between two camps: those who are looking to do numerics/data analytics with Python and those who are more on the web application side of things. The issues these groups encounter are quite different and their codes respond very differently to the remedies which are on offer.

    One of the biggest problems which has been holding research in the field is that of JIT fetishism. The heart of every project to improve the performance of Python — from Unladen Swallow, to PyPy, to Pyston — is that of using a JIT. The idea of course is nothing new. Well over a decade ago Python had a package (!) called Psyco which would generate specialised versions of blocks of Python code and JIT them. This was back in the day when most JavaScript runtimes were still walking the AST in order to do their evaluation and LLVM was still a research project.

    The observation that Python is not limited per-se by its interpreter is nothing new. For many years people have been observing that if one takes a piece of Python code and runs it through Cython (nee Pyrex) to generate C with an equivalent set of Python API calls that performance does not usually improve. Indeed, it is not usual for regressions to be observed simply because the Python interpreter knows all of the tricks when it comes to avoiding unnecessary API calls.

    Unfortunately, this mistaken idea that all Python needs is a JIT has been the death of many (non-numeric) attempts to improve Python’s performance. Rather, as the post highlights, the issue is due to the highly dynamic nature of Python. This costs us not only CPU cycles (spent fetching attributes and obtaining function pointers to potentially overloaded methods) but also burns through space in the L1-D cache.

    The natural approach within the JIT regime is to begin tracing the execution of small chunks of code and specializing based off of this. Although this has been done successfully with a variety of languages it is, especially for something as dynamic as Python, quite fiddly. But for me, the problem with what I described as JIT fetishism, is that it has caused the community to ignore other avenues for improving performance. Namely, the observation that while Python is a highly dynamic language that most applications — once they’ve reached a steady state of execution — are not. Hence, once started, they become amenable to top-down static analysis and classical optimization of the already existing bytecode. Things like avoiding attribute reloads, saving commonly used operators/methods to local variables, type inference (although this requires additional opcodes to fully exploit), function inlining, etc. If the interpreter ever does become a bottleneck there is always the possibility of switching to a register based interpreter rather than a stack based one which based off experience in the Java sphere can potentially yield 20% or so.

Cancel reply

No trackbacks yet.