Question about math.pi is mutable

Steven D'Aprano steve at pearwood.info
Sun Nov 8 21:23:06 EST 2015


On Mon, 9 Nov 2015 09:35 am, BartC wrote:

> Suppose this is the python program:
> 
> import m
> a=10
> b=20
> c=30
> m.f()
> 
> The set of global names the compiler knows will be ("m","a","b","c").

Wrong. Up to the line "c=30", the set of names the compiler can infer are m,
a, b and c. Once the line "m.f()" executes, *all bets are off*. The
compiler can no longer infer *anything* about those names. In principle,
m.f may have reached into the globals and deleted *any* of the names,
including itself.


> I don't believe code can remove these names (that would cause problems).

Of course names can be removed. There's even a built-in statement to do
so: "del".

Deleting names from namespaces is a moderately common thing to do.


> You say the set of global names can be added to - after this lot, but I
> can't see that the first four are going to change.

Of course they can change.


> Therefore these names could be referred to by index.

Perhaps. But that adds significant complexity to the compiler, and the
performance benefits *in practice* may not be as good as you imagine. After
all, there is usually far more to real programs than just getting and
setting names.

Nevertheless, such indexed name lookups do have the potential to save time,
and in fact that's what CPython already does with local variables. We can
get a *rough* indication of how big a difference this micro-optimization
makes by disabling it. Unfortunately, this only works in Python 2 -- in
Python 3, you can no longer defeat the local variable optimization.


def unopt():
    from math import *  # Defeats the local variable optimization.
    x = sin; x = cos; x = tan; x = exp; x = pi
    x = e; x = trunc; x = log; x = hypot; x = sqrt
    return

def opt():
    from math import sin, cos, tan, exp, pi, e, trunc, log, hypot, sqrt
    x = sin; x = cos; x = tan; x = exp; x = pi
    x = e; x = trunc; x = log; x = hypot; x = sqrt
    return

from timeit import Timer
t1 = Timer("unopt()", setup="from __main__ import unopt")
t2 = Timer("opt()", setup="from __main__ import opt")

# Best of five trials of 1,000,000 calls each.
print min(t1.repeat(repeat=5))
print min(t2.repeat(repeat=5))


When I run this code, I get

16.5607659817 seconds for unopt, and 3.58955097198 seconds for opt. That's a
significant difference. But remember that not all of that difference is due
to the name lookups (unopt also imports more stuff), and also remember that
this is a pretty unrealistic benchmark. Real functions do more than just
look up a variable name over and over.


> Attributes are harder because they are more of a free-for-all, but
> suppose you do have m.f().
> 
> "m" is easy, it's entry 0 in the global table for this module, so no
> lookup-by-name is needed. You examine it, and it's a module.
> 
> It's now necessary to find f within the global table for m. This is
> where it gets tricky if you want to avoid a lookup. And I expect you
> will say that any code can randomly add names within the global table of
> m.

Of course it can. It can even add names to builtins.

Oh, and you don't know that m is a module until you inspect it at runtime.
It could be any object.



> But bear with me. Suppose the interpreter were to maintain a table for
> each static (not runtime) attribute encountered in the source code. The
> compile can produce empty tables for the attributes it's seen. In this
> case the table for "f" would be empty: (). The compiler will also
> produce a list of such tables for all attributes.

I have no idea how well this implementation would work. My guess is that if
you look at, say, Nuitka, it may perform optimizations like this. (The aim
of Nuitka is to be a static optimizing compiler.) PyPy may do things like
this too, using a JIT optimizing compiler. PyPy is capable of far more than
such simple optimizations.

As I have argued all along, it is certainly not true that Python's
dynamicism prevents all such optimizations. It just makes them harder.


-- 
Steven




More information about the Python-list mailing list