ascenator ascenator - 4 months ago 10
Python Question

Which things can the Python interpreter optimize?

I think due to lazy evaluation statements like:

if True and True (...):
# do something


...should be skipped right after the
True and
part by the Python interpreter. However, in contrast to compiled Code, I think the Python interpreter can not optimize bad style like explicit boolean comparisions, right?

if condition == True:
# do something


A compiler would optimize this and delete the
== True
part, but the interpreter always has to evaluate which statements wait after the
condition ==
part, thus doing the unnecessary comparison of
== True
every time the code is executed?!

Do more of such pitfalls exist, where the interpreter can not optimize code? I know this last question is quite open, but I guess some popular examples do exist?

Answer

It's not much compilation per se (CPython does compile to bytecode), but the extremely dynamic nature of the language hampers "traditional" optimizations, which are guided by invariants which are difficult to check at compile time in Python, because the semantics of the language is such that most information about what the code is actually supposed to do is only available at runtime.

Your if condition == True: example is a good one: that comparison could be optimized only if the compiler could prove that condition is always a boolean, which is a non-trivial task if it derives from anything but literals in the current scope - and if it could prove that, at runtime, nobody managed to overwrite True with something else (possible only in Python 2 IIRC).

Mind you, some type inference is possible, and it's actually how code completion tools like Jedi work; however, Jedi can take some shortcuts and assume e.g. some kind of standard environment, while the interpreter has to cope with the most bizarre modifications to the globals that the language do allow.

So, in general CPython does not try very hard to optimize anything - actually, it doesn't seem to do any kind of "smart" code analysis, AFAICT they mostly try to build an efficient but "mostly vanilla" interpreter. So, expect to pay almost exactly for what you write, there's no optimizer to save sloppy code.

By the way, your and example is not an optimization done by the interpreter, it's just how the semantic of the operator is defined (and it's quite crucial that it's specified that and and or do short circuit evaluation and it's not an optional "optimization", since it could alter the observable behavior of the program if the right hand operand had side effects).

A better approach for a language that dynamic is instead that of a tracing JIT - the interpreter lets the code run for a while, and observes the invariants that seem to hold well (for example, condition seems to always be a boolean, True is always True, some variable is always an integer, ...), and then uses this information to compile to machine code an optimized version of the code. This is let run as long as the invariants above do hold - but as soon as one of the premises used to compile the optimized version is broken, the execution goes back into interpreted mode, and tracing begins again to find out if a new optimized version can be built.

Thus, the typical case is optimized well, applying the usual optimization techniques, but whenever something bizarre (but allowed by the language) happens, the runtime can fallback to the regular "literal" bytecode interpretation.

This is the approach used to squeeze good performance from most dynamic languages (most modern JavaScript engines use some variation of this basic approach), and in Python is used in PyPy.

Comments