[Python-Dev] Re: [Patch #102337] revised CALL_FUNCTION implementation

Jeremy Hylton jeremy@alum.mit.edu
Wed, 8 Nov 2000 19:14:45 -0500 (EST)


--QTUWfOmTKZ
Content-Type: text/plain; charset=us-ascii
Content-Description: message body text
Content-Transfer-Encoding: 7bit

I just uploaded a patch to sourceforge that revises the CALL_FUNCTION
implementation to use a bunch of functions and avoid the long block of
inline code in eval_code2.

The overall performance of the patch is about the same.  The current
patch causes a big slowdown for the use of keyword arguments, but is
otherwise as fast or faster then the old version.  The keyword
slowdown should be avoidable without too much effort.

I wrote a short benchmark that demonstrates the effect on many
variations of function calls.  The output lists the test time, and the
median, min, and max time of 10 executions.  (The benchmark script is
attached to this message.)

The vanilla CVS tree produces these results:

time_arg1 0.09 0.09 0.11
time_arg2 0.1 0.09 0.12
time_arg3 0.11 0.1 0.12
time_fact 0.12 0.1 0.14
time_meth 0.1 0.09 0.11
time_umeth 0.11 0.1 0.12
time_builtin 0.1 0.08 0.11
time_callable 0.37 0.33 0.38
time_keyword 0.14 0.13 0.18
time_star 0.12 0.12 0.14
time_kw 0.25 0.24 0.27
time_kw2 0.69 0.66 0.73
time_starkw 0.24 0.23 0.26
time_init 0.64 0.63 0.68
total 3.18

The CVS tree with the CALL_FUNCTION patch applied produces these
results: 
time_arg1 0.09 0.09 0.1
time_arg2 0.1 0.09 0.1
time_arg3 0.11 0.09 0.13
time_fact 0.11 0.11 0.14
time_meth 0.09 0.09 0.1
time_umeth 0.1 0.1 0.11
time_builtin 0.08 0.07 0.09
time_callable 0.35 0.34 0.38
time_keyword 0.42 0.4 0.44      (*** big slowdown ***)
time_star 0.13 0.13 0.15
time_kw 0.25 0.23 0.29
time_kw2 0.66 0.61 0.79
time_starkw 0.24 0.22 0.27
time_init 0.66 0.6 0.72
total 3.39

Jeremy


--QTUWfOmTKZ
Content-Type: text/plain
Content-Disposition: inline;
	filename="callbench.py"
Content-Transfer-Encoding: 7bit

import time

MANY = 40000
AFEW = 2000

def time_arg1(iters=range(MANY)):
    def arg1(x):
        return x

    t0 = time.clock()
    for i in iters:
        arg1(i)
    t1 = time.clock()
    return t1 - t0

def time_arg2(iters=range(MANY)):
    def arg2(x, y):
        return y

    t0 = time.clock()
    for i in iters:
        arg2(i, i)
    t1 = time.clock()
    return t1 - t0

def time_arg3(iters=range(MANY)):
    def arg3(x, y, z):
        return z

    t0 = time.clock()
    for i in iters:
        arg3(i, i, i)
    t1 = time.clock()
    return t1 - t0

def fact(n):
    if n == 0:
        return 1L
    else:
        return n * fact(n - 1)

def time_fact(iters=range(AFEW)):
    t0 = time.clock()
    for i in iters:
        fact(10)
    t1 = time.clock()
    return t1 - t0

class Foo:
    def method(self, x):
        return x

def time_meth(iters=range(MANY)):
    inst = Foo()
    meth = inst.method
    t0 = time.clock()
    for i in iters:
        meth(i)
    t1 = time.clock()
    return t1 - t0

def time_umeth(iters=range(MANY)):
    inst = Foo()
    meth = Foo.method
    t0 = time.clock()
    for i in iters:
        meth(inst, i)
    t1 = time.clock()
    return t1 - t0

def time_builtin(iters=range(MANY)):
    l = []
    func = l.count
    t0 = time.clock()
    for i in iters:
        func(i)
    t1 = time.clock()
    return t1 - t0

class Bar:
    def __call__(self, x):
        return x

def time_callable(iters=range(MANY)):
    inst = Bar()
    t0 = time.clock()
    for i in iters:
        inst(i)
    t1 = time.clock()
    return t1 - t0

def time_keyword(iters=range(MANY)):
    def kwfunc(a=None, b=None):
        return a + b

    t0 = time.clock()
    for i in iters:
        kwfunc(a=i, b=i)
    t1 = time.clock()
    return t1 - t0

def time_star(iters=range(MANY)):
    def star(a, b, c):
        return b

    arg = 1, 2, 3
    t0 = time.clock()
    for i in iters:
        star(*arg)
    t1 = time.clock()
    return t1 - t0

def time_kw(iters=range(MANY)):
    def kw(a=0, b=0):
        return a * b

    dict = {'a': 1, 'b': -1}
    t0 = time.clock()
    for i in iters:
        kw(**dict)
    t1 = time.clock()
    return t1 - t0

def time_kw2(iters=range(MANY)):
    def kw(a=0, b=0, **d):
        return d.values()[a]

    d = {'a':1, 'c':2, 'd':3}
    t0 = time.clock()
    for i in iters:
        kw(**d)
    t1 = time.clock()
    return t1 - t0

def time_starkw(iters=range(MANY)):
    def func(a, b, c=None):
        return b

    t = (1,)
    d = {'c':1}
    t0 = time.clock()
    for i in iters:
        func(i, *t, **d)
    t1 = time.clock()
    return t1 - t0

class Test:
    def __init__(self, arg=None):
        pass

def time_init(iters=range(MANY)):
    constructor = Test
    t0 = time.clock()
    for i in iters:
        constructor(arg=i)
    t1 = time.clock()
    return t1 - t0
    
def median(x):
    x.sort()
    return x[len(x)/2]

sum = 0.0
for func in (
    time_arg1, time_arg2, time_arg3, time_fact, time_meth,
    time_umeth, time_builtin, time_callable, time_keyword,
    time_star, time_kw, time_kw2, time_starkw, time_init,
    ):  
    times = [func() for i in range(9)]
    med = median(times)
    print func.func_name, med, min(times), max(times)
    sum += med
print "total", sum
    

--QTUWfOmTKZ--