Live data from Hacker News

Python performance myths and fairy tales

lwn.net

81–90 of 221 posts

Re: Python performance myths and fairy tales

#81

Python as a language will likely never have a "fast" implementation and still be Python. It is way too dynamic to be predictable from the code alone or even an execution stream in a way that allows you to simplify the actual code that will be executed at runtime either through AOC or JIT. The language is itself is also quite large in terms of syntax and built-in capability at this point which makes new feature-conple…

The secret as stated is the comlexity of a JIT. In practice, that dynamism just isn't used much in practice and in particular in optimization targets. The JIT analyses the code paths, sees that no writes to the target are possible so treats it as a constant. Java has similar levels of dynamism-with invokedynamic especially, but already with dynamic dispatch-in practice the JIT monomorphises to a single class even tho…

Yes, Java syntax might look like C++, but the execution semantics are closer to Objective-C and Smalltalk, which is why adopting StrongTalk JIT for Java Hotspot was such a win.

Re: Python performance myths and fairy tales

#82
post #22

Earlier quoted context omitted.

Because many never used Smalltalk, Common Lisp, Self, Dylan,... so they think CPython is the only way there is, plus they already have their computer resources wasted by tons of Electron apps anyway, that they hardly question CPython's performance, or lack thereof.

Has it ever crossed your mind that they just like Python?

And slow code, yes it has cross my mind.

Usually they also call Python to libraries that are 95% C code.

Re: Python performance myths and fairy tales

#83
post #49

The primary focus here is good and something I hadn't considered: python memory being so dynamic leads to poor cache locality. Makes sense. I will leave that to others to dig into. That aside, I was expecting some level of a pedantic argument, and wasn't disappointed by this one: "A compiler for C/C++/Rust could turn that kind of expression into three operations: load the value of x, multiply it by two, and then stor…

A “sufficiently smart compiler” can’t legally skip Python’s semantics. In Python, p.x * 2 means dynamic lookup, possible descriptors, big-int overflow checks, etc. A compiler can drop that only if it proves they don’t matter or speculates and adds guards—which is still overhead. That’s why Python is slower on scalar hot loops: not because it’s interpreted, but because its dynamic contract must be honored.

First, we need to add the word 'only': "not ONLY because it’s interpreted, but because its dynamic contract must be honored." Interpreted languages are slow by design. This isn't bad, it just is a fact.

Second, at most this describes WHY it is slow, not that it isn't, which is my point. Python is slow. Very slow (esp. for computation heavy workloads). And that is okay, because it does what it needs to do.

Re: Python performance myths and fairy tales

#84
post #80

Earlier quoted context omitted.

A “sufficiently smart compiler” can’t legally skip Python’s semantics. In Python, p.x * 2 means dynamic lookup, possible descriptors, big-int overflow checks, etc. A compiler can drop that only if it proves they don’t matter or speculates and adds guards—which is still overhead. That’s why Python is slower on scalar hot loops: not because it’s interpreted, but because its dynamic contract must be honored.

In Smalltalk, p x * 2 has that flow that as well, and even worse, lets assume the value returned by p x message selector, does not understand the * message, thus it will break into the debugger, then the developer will add the * message to the object via the code browser, hit save, and exit the debugger with redo, thus ending the execution with success. Somehow Smalltalk JIT compilers handle it without major issues.

Smalltalk JITs make p x * 2 fast by speculating on types and inserting guards, not by skipping semantics. Python JITs do the same (e.g. PyPy), but Python’s dynamic features (like __getattribute__, unbounded ints, C-API hooks) make that harder and costlier to optimize away.

You get real speed in Python by narrowing the semantics (e.g. via NumPy, Numba, or Cython) not by hoping the compiler outsmarts the language.

Re: Python performance myths and fairy tales

#86
post #16

I think an important bit of context here is that computers are very, very good at speculative happy-path execution. The examples in the article seem gloomy: how could a JIT possibly do all the checks to make sure the arguments aren’t funky before adding them together, in a way that’s meaningfully better than just running the interpreter? But in practice, a JIT can create code that does these checks, and modern proces…

> but v8 has made common use cases extremely fast. I’m excited for the future of Python.

Isn't v8 still entirely single threaded with limited message passing? Python just went through a lot of work to make multithreaded code faster, it would be disappointing if it had to scrap threading entirely and fall back to multiprocessing on shared memory in order to match v8.

Re: Python performance myths and fairy tales

#87

The most interesting part of this article is the link to SPy. Attempts to find a subset of python that could be made performant.

Honestly that seems Sisyphean to me. The market doesn't want a "performant subset". The market is very well served by performant languages. The market wants Python's expressivity. The market wants duck typing and runtime-inspectable type hierarchies and mutable syntax and decorators. It loves it. It's why Python is successful.

My feeling is that numba has exactly the right tactic here. Don't try to subset python from on high, give developers the tools[1] so that they can limit themselves to the fast subset, for the code they actually want. And let them make the call.

(The one thing numba completely fails on though is that it insists on using its own 150+MB build of LLVM, so it's not nearly as cleanly deployable as you'd hope. Come on folks, if you use the system libc you should be prepared to use the system toolchain.)

[1] Simple ones, even. I mean, to first approximation you just put "@jit" on the stuff you want fast and make sure it sticks to a single numeric type and numpy arrays instead of python data structures, and you're done.

Re: Python performance myths and fairy tales

#88
post #51

So we are paying 99% of the performance just for the 1% of cases where it's nice to code in. Why do people think it's a good trade-off?

Performance is worthless if the code isn't correct. It's easier to write correct code reasonably quickly in Python in simple cases (integers don't overflow like in C, don't wrap around like in C#, no absurd implicit conversions like in other scripting languages). Also you don't need code to be fast a lot of the time. If you just need some number crunching that is occasionally run by a human, taking a whole second is…

I mean you can see it with your own experience, folks will post a 50 line snippet of ordinary C code in an blog post which looks like you're reading a long dead ancient language littered with macros and then be like "this is a lot to grok here's the equivalent code in Python / Ruby" and it's 3 lines and completely obvious.

Folks on HN are so weird when it comes to why these languages exist and why people keep writing in them. For all their faults and dynamism and GC and lack of static typing in the real world with real devs you get code that is more correct written faster when you use a higher level language. It's Go's raison d'etre.

Re: Python performance myths and fairy tales

#89
post #86
post #16

I think an important bit of context here is that computers are very, very good at speculative happy-path execution. The examples in the article seem gloomy: how could a JIT possibly do all the checks to make sure the arguments aren’t funky before adding them together, in a way that’s meaningfully better than just running the interpreter? But in practice, a JIT can create code that does these checks, and modern proces…

> but v8 has made common use cases extremely fast. I’m excited for the future of Python. Isn't v8 still entirely single threaded with limited message passing? Python just went through a lot of work to make multithreaded code faster, it would be disappointing if it had to scrap threading entirely and fall back to multiprocessing on shared memory in order to match v8.

Multithreaded code is usually bottlenecked by memory bandwidth, even more so than raw compute. C/C++/Rust are great at making efficient use of memory bandwidth, whereas scripting languages are rather wasteful of it by comparison. So I'm not sure that multithreading will do much to bridge the performance gap between binary compiled languages and scripting languages like Python.

Re: Python performance myths and fairy tales

#90
post #16

I think an important bit of context here is that computers are very, very good at speculative happy-path execution. The examples in the article seem gloomy: how could a JIT possibly do all the checks to make sure the arguments aren’t funky before adding them together, in a way that’s meaningfully better than just running the interpreter? But in practice, a JIT can create code that does these checks, and modern proces…

I wonder if branch prediction can still hide the performance loss when the happy path checks become large/complex. Branch prediction is a very low level optimisation. And if the predictor is right you don't get everything for free. The CPU must still evaluate the condition, which takes resources, albeit it's no longer on the critical path. However I'd think the CPU would stall if it got too far ahead of the condition execution (ultimately all the code must execute before the program completes). Perhaps given the nature of Python, the checks would be so complex that in a tight loop they'd exert significant resource pressure?
Post reply on HN