Calling a function is faster than not calling it?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun May 10 11:04:12 EDT 2015


On Sun, 10 May 2015 08:34 pm, Christian Gollwitzer wrote:

> Am 10.05.15 um 11:58 schrieb Steven D'Aprano:
>> Why is calling a function faster than bypassing the function object and
>> evaluating the code object itself? And not by a little, but by a lot?
>>
>> Here I have a file, eval_test.py:
>>
>> # === cut ===
>> from timeit import Timer
>>
>> def func():
>>      a = 2
>>      b = 3
>>      c = 4
>>      return (a+b)*(a-b)/(a*c + b*c)
>>
>>
>> code = func.__code__
>> assert func() == eval(code)
>>
>> t1 = Timer("eval; func()", setup="from __main__ import func")
>> t2 = Timer("eval(code)", setup="from __main__ import code")
>>
>> # Best of 10 trials.
>> print (min(t1.repeat(repeat=10)))
>> print (min(t2.repeat(repeat=10)))
> 
> what exactly does this mean, are you calling it 10 times? Are you sure
> hat is enaough to reach the granularity of your clock? A benchmark
> should last at least 100ms IMHO.


No, timeit by default calls the code snippet one million times. So in the
example above, each trial calls "eval(code)" one million times, and I have
ten trials. I pick the fastest trial, and report that.

The time for the fastest trial is 1.7 seconds for one million calls to eval.


>> # === cut ===
>>
>>
>> Note that both tests include a name lookup for eval, so that as much as
>> possible I am comparing the two pieces of code on an equal footing.
>>
>> Here are the results I get:
>>
>>
>> [steve at ando ~]$ python2.7 eval_test.py
>> 0.804041147232
>> 1.74012994766
>> [steve at ando ~]$ python3.3 eval_test.py
>> 0.7233301624655724
>> 1.7154695875942707
>>
>> Directly eval'ing the code object is easily more than twice as expensive
>> than calling the function, but calling the function has to eval the code
>> object. That suggests that the overhead of calling the function is
>> negative, which is clearly ludicrous.
>>
>> I knew that calling eval() on a string was slow, as it has to parse and
>> compile the source code into byte code before it can evaluate it, but
>> this is pre-compiled and shouldn't have that overhead.
> 
> Does eval(code) lookup a,b and c in the scope of the eval, whereas in
> the func it is bound to locals and optimized out?

Obviously I don't *fully* understand the details either, otherwise I
wouldn't be asking, but as far as I understanding, in my example above:

def func():
    a = 2
    b = 3
    c = 4
    return (a+b)*(a-b)/(a*c + b*c)

the function's code object includes a reference to the three constants (2,
3, 4, also None) and the local variable look-ups just grab them from the
internal cache:

func.__code__.co_consts
=> returns (None, 2, 3, 4)

So I can set globals with the same name, then eval the code object, and
still get the right answer:

py> a, b, c = 100, 50, 25
py> func()
-0.25
py> eval(func.__code__)
-0.25



-- 
Steven




More information about the Python-list mailing list