Question about math.pi is mutable

Steven D'Aprano steve at pearwood.info
Tue Nov 10 06:34:16 EST 2015


On Tue, 10 Nov 2015 05:10 pm, Ben Finney wrote:

> Steven D'Aprano <steve at pearwood.info> writes:
> 
>> Ben, I fear that you are not paying attention to me :-)
> 
> Possibly, though I also think there's miscommunication in this thread.
> 
> You speak of “compile time” and “run time”. You also speak of what the
> compiler can do, at run time.
> 
> I am a Bear of Little Brain, but: Isn't anything that the *compiler*
> does, by definition done at *compile* time?

In a manner of speaking, yes, of course. But you've missed the critical
issue: when is compile time?

If you're like most people, you probably are thinking about an execution
model where the compiler analyses the source code statically, using nothing
but what can be seen in the source code, then hands over some sort of
compiled byte code to be run by an interpreter or machine code that is
executed by the CPU. The compiler and interpreter are completely distinct.

If so, you're stuck with an obsolete model of computation, like somebody
trying to understand modern chemistry based on the "planetary orbit" model
of the atom.

So when is compile time? Of course, in some languages (like C, or Pascal)
compilation occurs as a distinct stage before you can run the code. But
that's not necessarily true for all compilers. "Just In Time" compilers
operate while the program is running, compiling code just before it is
executed. The distinction between compiler and interpreter is gone, or at
least weakened.

Python -- yes, even CPython -- has a runtime compiler. When you import a
module, it is compiled (if needed) just before the import. Likewise, when
you call the `compile`, `eval` or `exec` built-ins, the compiler operates.

I'm not calling this a JIT compiler, because the simple-minded compilation
performed by `compile` etc doesn't use any run-time information. It just
statically compiles the code to byte-code.

But the fact that it happens *at runtime* is significant, because in
principle there is a lot more information available to the compiler, if it
were intelligent enough to make use of it. For example, suppose you execute
this Python snippet:

result = x + 2

At static compile time, looking just at the source, you may not know what
value x is, or even whether or not x actually exists, so you're forced to
go through the standard Python semantics to determine what the result is:

py> from dis import dis
py> dis("result = x + 1")
  1           0 LOAD_NAME                0 (x)
              3 LOAD_CONST               0 (1)
              6 BINARY_ADD
              7 STORE_NAME               1 (result)
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE


But a JIT compiler gets to compile code right before that line is due to
execute. By the time the JIT compiler gets to see that line of code, it can
already know whether or not name "x" exists, and if so, which namespace it
is in. It knows what value "x" has. The compiler can choose what byte code,
or even machine code, to generate, using information available just before
that code is needed.

Suppose it knows that "x" is bound to an float. Then it can cast the int 1
to the machine floating point double 1.0 and generate code that adds that
to a float. That's likely to be tens of times faster than the BINARY_ADD
op-code, which has to do a whole lot of type-checking, method calling, and
creating and disposing of objects to add the two values.

Only if "x" is unknown, or if it is some type that doesn't have a convenient
optimization, does the JIT compiler generate the standard (but unoptimized)
BINARY_ADD op-code.

Provided that, on average, the book-keeping needed by the JIT compiler is
outweighed by the gains, the whole process counts as a win.

Now, CPython doesn't include a JIT compiler. But PyPy does, and it is much
more sophisticated than anything I could explain. I'm not the only one:

https://glyph.twistedmatrix.com/2012/02/this-isnt-how-pypy-works-but-it-might.html

If you remember Psycho, that might help. Psycho (according to its creator)
isn't a "true" JIT compiler, but it's close enough. Psycho would generate
machine code -- actual low level code running in the CPU or FPU, not byte
code -- for certain Python operations, plus guard code that ensured the
machine code was only called when it was safe to do so. So this is long
proven technology.

More about JIT compilation on Wikipedia:

https://en.wikipedia.org/wiki/Just-in-time_compilation



-- 
Steven




More information about the Python-list mailing list