Timing and aspect oriented programming (Was Re: Equivalent of Perl chomp?)

Huaiyu Zhu huaiyu at gauss.almadan.ibm.com
Fri Feb 1 14:03:15 EST 2002


On Thu, 31 Jan 2002 12:29:06 -0800, Mark McEahern <marklists at mceahern.com>
wrote: 
>
>I timed them with the following code and it occurred to me here's a possibly
>good case where aspect-oriented programming would be nice. 

I tried the example with aspect oriented programming.  In this case the cost
is too high, but it might be useful in some other cases.  Following are the
test program, the results, and the aspect module.


#-------------- test_ends.py -----------------------

from time import time

a = "das af dsa fdsaa fdsaf as das as fdas as fsa as dsa fadasfdsa "
x = "adsfasdf";
n = 10000; 

#------------------------------------------------------------------
# plain old test

def slice(a, x, n):
    _time = time()
    for i in xrange(n):
        #a[-1:] == x
        a[-len(x):] == x
    return time() - _time

def endswith(a, x, n):
    _time = time()
    for i in xrange(n):
        a.endswith(x)
    return time() - _time

print ; print "slice endswith"
for i in range(5):  print slice(a, x, n), endswith(a, x, n)
print ; print "endswith slice"
for i in range(5):  print endswith(a, x, n), slice(a, x, n)

#------------------------------------------------------------------
# aspect test

class Test:
    def slice(self, a, x):
        a[len(x):] == x
    
    def endswith(self, a, x):
        a.endswith(x)

from aspect1 import timeit
timeit(Test, 'slice', n)
timeit(Test, 'endswith', n)
t = Test()

print ; print "slice endswith"
for i in range(5):  print t.slice(a, x), t.endswith(a, x)
print ; print "endswith slice"
for i in range(5):  print t.endswith(a, x), t.slice(a, x)

# --------- the results
When slice uses [-1:]

slice endswith
0.226076006889 0.30391895771
0.21536898613 0.293772935867
0.214552998543 0.296599984169
0.215177059174 0.301518917084
0.214749097824 0.296058058739

endswith slice
0.287701964378 0.2228089571
0.288692951202 0.231354951859
0.28769505024 0.222257971764
0.287680029869 0.22406899929
0.287147045135 0.228953003883

When slice uses [-len(x):]

slice endswith
0.0638729333878 0.0270719528198
0.0566090345383 0.0265839099884
0.0572431087494 0.0265699625015
0.0570579767227 0.0334140062332
0.0567760467529 0.0265719890594

endswith slice
0.0266000032425 0.0571140050888
0.0265990495682 0.0568480491638
0.0266120433807 0.0567020177841
0.026643037796 0.056519985199
0.0268899202347 0.0658220052719


When slice uses [-len(x):], using "aspect-oriented" timing:

slice endswith
0.122717022896 0.0866810083389
0.122745990753 0.0862590074539
0.13433098793 0.0857419967651
0.121955990791 0.0860439538956
0.129957079887 0.0868830680847

endswith slice
0.0857659578323 0.121948957443
0.085440993309 0.12923002243
0.0860010385513 0.122326016426
0.0858659744263 0.123429894447
0.0925229787827 0.121724009514


Here's the aspect module used:

# --------------------------------- aspect1.py ----------------------

#! /usr/bin/env python
# --------------------------------------------------------------------------
# aspect.py
# 
# From: Pedro Rodriguez (pedro_rodriguez at club-internet.fr)
# Newsgroups: comp.lang.python
# Date: 2002-01-13 02:27:51 PST
# Date: Tue, 22 Jan 2002 16:29:18 +0100
#
# Modified: Huaiyu Zhu 2002-01-27, 2002-01-30

# --------------------------------------------------------------------------
# The core functions

from __future__ import nested_scopes

def wrap(aspect, wrapper, cls, *method_names):
    #print "wrap", aspect, wrapper, cls, method_names
    for method_name in method_names:
        orig_method = getattr(cls, method_name)
        wrapped_method = aspect(wrapper, orig_method, method_name)
        setattr(cls, method_name, wrapped_method)

def around(wrapper, orig_method, method_name):
    def wrapped_method(inst, *args, **kwargs):
        return wrapper(inst, orig_method, method_name, *args, **kwargs)
    return wrapped_method

def before(wrapper, orig_method, method_name):
    def wrapped_method(inst, *args, **kwargs):
        wrapper(inst, orig_method, method_name, *args, **kwargs)
        result = orig_method(inst, *args, **kwargs)
        return result
    return wrapped_method

def after(wrapper, orig_method, method_name):
    def wrapped_method(inst, *args, **kwargs):
        result = orig_method(inst, *args, **kwargs)
        wrapper(inst, orig_method, method_name, *args, **kwargs)
        return result
    return wrapped_method 

# ------------------------------------------------------------------
# Example aspects

def log_around(cls, method_name, file=None):
    def wrapper(inst, method, method_name, *args, **kwargs):
        print >> file, "starting:", inst, method_name, args, kwargs
        result =  method(inst, *args, **kwargs)
        print >> file, "stopping:", inst, method_name
        return result
    wrap(around, wrapper, cls, method_name)

def log_before(cls, method_name, file=None):
    def wrapper(inst, method, method_name, *args, **kwargs):
        print >> file, "before:", inst, method_name, args, kwargs
    wrap(before, wrapper, cls, method_name)

def log_after(cls, method_name, file=None):
    def wrapper(inst, method, method_name, *args, **kwargs):
        print >> file, "after:", inst, method_name, args, kwargs
    wrap(after, wrapper, cls, method_name)

def err_abort(cls, method_name, file=None):
    def wrapper(inst, method, method_name, *args, **kwargs):
        try:
            return method(inst, *args, **kwargs)
        except Exception, e:
            print >> file, "Error abort: %s.%s(*%s, **%s)" % (
                inst, method_name, args, kwargs), e
            raise SystemExit
    wrap(around, wrapper, cls, method_name)

def err_ignore(cls, method_name, file=None):
    def wrapper(inst, method, method_name, *args, **kwargs):
        try:
            return method(inst, *args, **kwargs)
        except Exception, e:
            print >> file, "Error ignore: %s.%s(*%s, **%s)" % (
                inst, method_name, args, kwargs), e
            return None
    wrap(around, wrapper, cls, method_name)

def timeit(cls, method_name, num=100):
    from time import time
    def wrapper(inst, method, method_name, *args, **kwargs):
        _time = time()
        for i in xrange(num):
            method(inst, *args, **kwargs)
        return time() - _time
    wrap(around, wrapper, cls, method_name)
    
# ------------------------------------------------------------------
# Example applications

if __name__ == "__main__":
    
    class A:
        def f1(self, x, y):
            s =  "f %s %s" % (x, y)
            print " - inside", s; return s
    
        def g1(self, x):
            s = "g %s" % x
            print " - inside", s; return s

        def h1(self, x):
            s = "h %s" % (1/x)
            print " - inside", s; return s

        def i1(self, x):
            s = "i %s" % (1/x)
            print " - inside", s; return s
    
    class B:
        def z(self, *args, **kwargs):
            s = "z %s %s" % (str(args), str(kwargs))
            print " - inside", s; return s
                    
    log_around(A, "h1")
    log_before(A, "g1")
    err_abort(A, "h1")
    err_ignore(A, "i1")
    log_after(B, "z")
    log_around(B, "z")

    class C(A): pass
    
    a = C()
    print a.f1(1,2)
    print a.g1(3)
    
    print
    
    b = B()
    print b.z(1,2,3, x=42)
    print b.z(4)

    print 
    print a.h1(3)
    print a.i1(0)
    print a.h1(0)
    print "Should not get here"





More information about the Python-list mailing list