Overhead for local assignments (was: A better self)

Louis M. Pecora pecora at anvil.nrl.navy.mil
Tue Jul 23 12:11:00 EDT 2002


I did some tests as suggested in the "Better self" thread on the timing
overhead of assigning to local variables and then using them in a
formula.  The issue in that thread was readability which can be gained
several ways. The code and results below compares those ways.  I
present the explanation for the four tests, first, then show the
results (a bit surprising...to me, at least), and then at the end I
give the code.  I am not claiming this is the best way to do these
tests.  It is typical of coding I have done. Feel free to criticize or
better, suggest other tests, or even better, do other tests and post
them.  

  -- Lou Pecora

EXPLANATION OF TESTS =====================================

#  Tests overhead for "assigning" object variables 
#  (e.g. self.t, self.x, self.y, self.z) to
#  local variables or function arguments so as to use simpler names 
#  in a formula without the "self." prefix.  Such assignements 
#  will cause "overhead".  This module's aim is to compare the 
#  overhead involved in each approach.  
#  The approaches are:
#  (1)  Assign in tuple, thus:  t,x,y,z=self.t,self.x,self.y,self.z, 
#       then use t,x,y, and z in the formula
#  (2)  Assign in line, thus:   t=self.t
#                               x=self.x
#                               y=self.y
#                               z=self.z, then use t,x,y, and z
#       in the formula
#  (3) Assign to class function arguments, thus:
#      self.fcntst(self.t,self.x,self.y,self.z),
#      where the class function contains the formula
#  (4) Assign to class function arguments using local 
#      variable f assigned to the class function, thus:
#      f(self.t,self.x,self.y,self.z), where f=self.fcntst
#
#  Note, an attempt is made to _exclude_ the loop overhead by 
#  timing an empty xrange loop, first, and subtracting that 
#  time, tx, from the tests (1) to (4)

RESULTS AND COMMENTS =====================================

Note:  the results (the ratios) appeared to be independent of the
number of loops used (n, below) from n=1000 to n=1000000

xRange time= 0.15076
tuple assign time= 0.958203999999
line time= 0.799539
fcn1 (class call) time= 1.508272
fcn2 (local call) time= 1.256834

Ratios to the minimum time:
(1): 1.24455939542
(2): 1.0
(3): 2.09241051267
(4): 1.70485481189

Method (2) is fastest as some people suggested.  (1) does seem to
involve some time creating and destroying a tuple as suggested.  (3) is
the worst, by a factor of 2!  So if you have lots of class function
calls, assign the function to a local variable, but even better, avoid
using the function calls to just calculate a formula, if possible.

CODE =====================================

# Just imports stuff commonly used
import time
from Numeric import *

class lcltst:
   
   def __init__(self,n):
      self.t=1
      self.x=2
      self.y=3
      self.z=4
      self.n=n   # Numb.loops
      # Calculate the for loop time so as to exclude it later
      t1=time.clock()
      for i in xrange(self.n):
         pass
      self.tx=time.clock()-t1 # loop time
      print "xRange time=",self.tx
   
   # (1) Assign variables in a tuple
   def linasg(self):
      t1=time.clock()
      for i in xrange(self.n):
         t,x,y,z=self.t,self.x,self.y,self.z    
         # After assigning local variables normally the 
         # formula for doing
         # calculations using the local variables would follow
      t2=time.clock()
      print "tuple assign time=",t2-t1
      return t2-t1

   # (2) Assign variables one at a time, in line
   def indasg(self):
      t1=time.clock()
      for i in xrange(self.n):
         t=self.t
         x=self.x
         y=self.y
         z=self.z
         # After assigning local variables normally the 
         # formula for doing
         # calculations using the local variables would follow
      t2=time.clock()
      print "line time=",t2-t1
      return t2-t1

   # Dummy function
   def fcntst(self,t,x,y,z):
      # Would normally contain the formula and do calculations using 
      # the argument variables
      pass

   # (3) Call dummy formula function
   def fcn1(self):
      t1=time.clock()
      for i in xrange(self.n):
         # Use class function call
         self.fcntst(self.t,self.x,self.y,self.z)
      t2=time.clock()
      print "fcn1 (class call) time=",t2-t1
      return t2-t1

   # (4) Call local assignment of dummy formula function
   def fcn2(self):
      t1=time.clock()
      # Assign class function to local variable, first
      f=self.fcntst
      for i in xrange(self.n):
         f(self.t,self.x,self.y,self.z)
      t2=time.clock()
      print "fcn2 (local call) time=",t2-t1
      return t2-t1

#---- Run the test for n loops ----------------------------

print "--------------------------------------"

n=200000   # Number of loops (tested from n=1000, to n=1000000)
lt=lcltst(n)
tx=lt.tx
tlist=[lt.linasg()-tx]
tlist=tlist+[lt.indasg()-tx]
tlist=tlist+[lt.fcn1()-tx]
tlist=tlist+[lt.fcn2()-tx]
tmax=min(tlist)
tfract=divide(tlist,tmax)
print 
print "Ratios to the minimum time:"
print "(1):",tfract[0]
print "(2):",tfract[1]
print "(3):",tfract[2]
print "(4):",tfract[3]



More information about the Python-list mailing list