Python IS slow ! [was] Re: Python too slow for real world

Michael Hudson mwh21 at cam.ac.uk
Sun May 2 11:12:56 EDT 1999


Christian Tismer <tismer at appliedbiometrics.com> writes:
> Michael Hudson wrote:
> 
> [speeup for functors, too?]
> 
> > It seems so. I've found something to do with my starship page beyond
> > basic fiddling at last; go to http://starship.python.net/crew/mwh to
> > find a little tarball containing closure.py from my last post and
> > xapply.py. xapply.py implements functor.xapply for the simplest cases;
> > it ignores varadic details and keyword arguments entirely. It does
> > work for methods (bound and unbound).
> > 
> > I haven't timed it, but so far as I understand, it should implement
> > partial argument resolution with slowdown by a factor of 1 :-).
> 
> Now, that's interesting. In all those insane speed
> races, we always came up with solutions like
> 
> def my_lightspeed_impl(arg1, arg2,      None=None, map=map, ...):
> 
> to avoid global lookups. They became default values
> for not otherwise used parameters.
> Big Q: Is your method even faster?
> Now, I did an extra benchmark and it turns out that
> the old default parameter trick is still a few cycles
> faster.
> Possible improvements:
> As you can see with my benchmark, it would be nice
> to take the chance and allow to change the function
> name when binding.

That's easy-peasy:

def xapply_func(func,args,newname=None):
    if newname is None:
        newname=func.func_name
    newfunc=new.function(
        munge_code(func.func_code,args),
        func.func_globals,
        newname)
    newfunc.__doc__=func.__doc__
    newfunc.func_defaults=func.func_defaults
    newfunc.func_doc=func.func_doc
    return newfunc

or

def bind(func,newname=None,**vars):
    if newname is None:
        newname=func.func_name
    newfunc=new.function(
        munge_code(func.func_code,vars),
        func.func_globals,
        newname)
    newfunc.__doc__=func.__doc__
    newfunc.func_defaults=func.func_defaults
    newfunc.func_doc=func.func_doc
    return newfunc

(unless you want to bind a variable called newname or func, of
course. I guess you could mutilate bind.func_code so that Python
thinks the first two parameters have names that are illegal
identifiers. Hmm.)

> Then, what about indirections? I used to use default
> parameter assignments like  add=operator.add
> which I now would have to do elsewhere to get rid
> of that indirection. Do you see a chance to bind
> these through as well, maybe for builtins only, or
> for surely read-only attributes?

That would be nice, and I have thought about it, but you're replacing

	LOAD_GLOBAL	('operator')
	LOAD_ATTR	('add')

which is six bytes with

	LOAD_CONST	(n)

which is just three bytes. So, as I think I said earlier, you'd need
to recompute jumps, and I haven't yet had the bloody-mindedness to get
that to work.

It'll probably happen though. Skip Montanaro has written a peephole
optimizer for Python
(http://www.automatrix.com/~skip/python/spam7/optimizer.html); this
must do these manipulations, so maybe I'll have a look at that for
inspiration. 

The similarity of the code in xapply.py and in closure.py makes me
think that I should write some general framework for mutilating
codestrings. Support for changing the length of the codestring is
pretty much a must for that.

> What might the reason be for the slight slowdown
> between load_const and default parameter access?
> load_fast appears to be a little quicker.

Poking around the source shows that a LOAD_FAST just indexes into an
array, whereas LOAD_CONST indexes into a Python tuple. Fast, but still
a couple of C function calls of indirection on top of LOAD_FAST. 

The upshot of this is that:

def func0(x):
    return x + 0 + 0 + 0 + 0 + 0

is slower than

def func1(x, g1=g1, g2=g2, g3=g3, g4=g4, g5=g5):
    return x + g1 + g2 + g3 + g4 + g5

(!)

The time differentials we're talking about here are very, very small..

> Could it be even better to turn it upside down
> and invent hidden default parameters instead? :-)

I don't know what the implementation details of making co_consts a
bare C array of PyObject*s would be. It wouldn't be hard, but I don't
really think it's worth the effort.

I've tweaked the benchmark slightly and added it to the tarball on the
starship.

> thanks & cheers - chris

A pleasure!

Michael




More information about the Python-list mailing list