Tibor's Musings

Python Method Call Overhead

Python function calls are expensive. Python object oriented method calls are even more expensive. How can we estimate method call overhead?

By measuring performance of functional redefinition vs performance of class method calls.

One can find out that the overhead of using the OO method calls over functional calls seems to be about 10%; see the test code below. Also, while the cost of functional redefinition is practically zero, one more OO subclass level adds further ~1%, so that the cost of OO one-subclass inheritance redefinition over functional redefinition is about 11%.

Hence, if one chooses OO, it is interesting to keep the class hierarchy tree as simple as possible, and the cost of OO will be about 10%. In practice, this is often acceptable.

(Note that this does not say much about the slowness of Python OO method call performance for simple class precedence trees, but rather about the slowness of the classical Python function call.)

Here is redef_test.py testing code:

#!/usr/bin/env python

"""
Simple testing of performance cost of functional redefinition vs OO.

Results on PCDH23 run on 20041118 are:

   $ ./redef_test
   testing performance cost of functional redefinition vs OO ...
   fun 5.21
   fun_redef 5.1
   fun_oo 5.68
   $ ./redef_test
   testing performance cost of functional redefinition vs OO ...
   fun 5.21
   fun_redef 5.11
   fun_oo 5.66
   $ ./redef_test
   testing performance cost of functional redefinition vs OO ...
   fun 5.19
   fun_redef 5.1
   fun_oo 5.67

This means that the function call cost in case of functional
redefinition is zero, and in case of OO for a simple one-level class
hierarchy is 11%.  Of course it will be more in case of deeper/complex
class precedence lists, but I haven't measured how much.

P.S. On 20041122 were added fun_oo_subclass to confirm that fun_oo and
     fun_oo_subclass give the same results, as it's mostly due to the
     OO overhead.  One subclass does not add anything big to the
     overhead; I measured it to be ~1% difference.
"""

import time
from redef_test_slave import fun, fun_redef, fun_oo, fun_oo_subclass

def timing(f, a, n=1000000):
    """Return timing of function F on argument A run N times.
       Taken from <http://www.python.org/doc/essays/list2str.html>.
    """
    print f.__name__,
    r = range(n)
    t1 = time.clock()
    for i in r:
        f(a); f(a); f(a); f(a); f(a); f(a); f(a); f(a); f(a); f(a)
    t2 = time.clock()
    print round(t2-t1, 3)
    return

def main():
    """Simple testing of performance cost of functional redefinition vs OO.
    """
    print "testing performance cost of functional redefinition vs OO ..."
    timing(fun, 3)
    timing(fun_redef, 3)
    timing(fun_oo().fun_oo, 3)
    timing(fun_oo_subclass().fun_oo_subclass, 3)
    return

if __name__ == '__main__':
    main()

Here is redef_test_slave.py:

#!/usr/bin/env python

def fun(x):
    return

def fun_redef(x):
    import sys
    sys.exit(1)

def fun_redef(x):
    return

class fun_oo:
    def fun_oo(self, x):
        return
    def fun_oo_subclass(self, x):
        import sys
        sys.exit(1)

class fun_oo_subclass(fun_oo):
    def fun_oo_subclass(self, x):
        return

python