[Python-Dev] optimizing non-local object access
Skip Montanaro
skip@pobox.com (Skip Montanaro)
Thu, 9 Aug 2001 11:28:36 -0500
I had a thought last night about optimizing access to globals in other
modules (e.g. "math.sin" or "string.uppercase"). I think it could actually
work to speed up all non-local object access.
The nice thing about module globals is that for the most part they are
"almost constant". Once a module is imported, it's rare that its global
name bindings actually change. I think it might suffice to create a
notification system that allows an executing frame to register its interest
in changes to a module's name bindings.
Let me make this idea concrete with an example. Suppose I have the
following code:
import math
def sinseq(n):
l = []
for i in xrange(n):
l.append(math.sin(i))
return l
"math.sin" requires a global lookup and an attribute access each pass
through the loop. Let's add an entry to the fastlocals array for it and an
entry to the local names called "math.sin". The byte code compiler
generates a LOAD_FAST instruction anywhere math.sin is accessed, and at the
first point where it should be "live" (i.e., at the beginning of the
function) it simply inserts a TRACK_OBJECT instruction with that slot in
fastlocals as one argument and the "math.sin" slot in the local names as the
other. When "math.sin" goes out of scope (i.e., at the end of the
function), it inserts an UNTRACK_OBJECT instruction with "math.sin" as its
argument. The same can be done for "l.append" and xrange. The bytecode for
the above function would look something like:
TRACK_OBJECT math.sin
TRACK_OBJECT xrange
>> 0 BUILD_LIST 0 ('\000', '\000')
3 STORE_FAST 1 (l)
TRACK_OBJECT l.append
6 SETUP_LOOP 44 (to 53)
9 LOAD_FAST xrange
12 LOAD_FAST 0 (n)
15 CALL_FUNCTION 1 ('\001', '\000')
18 LOAD_CONST 1 (0)
21 FOR_LOOP 28 (to 52)
24 STORE_FAST 2 (i)
27 LOAD_FAST l.append
LOAD_FAST math.sin
39 LOAD_FAST 2 (i)
42 CALL_FUNCTION 1 ('\001', '\000')
45 CALL_FUNCTION 1 ('\001', '\000')
48 POP_TOP
49 JUMP_ABSOLUTE 21 ('\025', '\000')
>> 52 POP_BLOCK
>> 53 LOAD_FAST 1 (l)
UNTRACK_OBJECT l.append
UNTRACK_OBJECT xrange
UNTRACK_OBJECT math.sin
56 RETURN_VALUE
TRACK_OBJECT and UNTRACK_OBJECT are responsible for registering and
unregistering interest in an object. TRACK_OBJECT also caches the
corresponding object in the fastlocals array.
<wave what="hands">
I think most or all of the work can be handled by PyObject_SetAttr and/or
PyDict_SetItem. It seems to me that its greatest benefit would be to short
circuit access to global variables in other modules. For the most part,
these don't change once an import is completed, so you'd effectively be
converting access to these objects into local variables, but you could use
it to track all non-local variables or attributes.
</wave>
stepping-behind-the-egg-proof-screen-ly y'rs,
Skip