Python object method versus external function speed

On a plane from Qld I came up with all sorts of clever OO ways to refactor my particle system. Unfortunately it looks as though the cost is too high. Not by a great deal, but for a realtime app that I want to squeeze as much out as possible it’s (just barealy) not acceptable.

In short, calling traverse(neighbour_list) is around 8% faster than calling neighbour_list.traverse(). It appears that it’s better (faster, anyway) to write mutator methods external to the class they need to work on.

Still thinking about this one – it’s quite possible that once all the maths involved with each interaction or force contribution is included, the fractional cost will get lower are lower (I’m prepared to accept ~%2, I think. Yeah, 2 percent is a nice, arbitrary small number).

–edit–
Starting to come to my senses. 10% performance hit is actually quite acceptable, especially given this program is all about playing and prototyping. Being able to dynamically add and remove forces, and for each force to have its own independent (or shared) neighbour list is important.

Better to have a convenient design, and use techniques such as pointed out here http://wiki.python.org/moin/PythonSpeed/PerformanceTips for speeding up. The most relevent seem to be: using local variables (e.g. assign force.apply() to apply(), before the loop body) as python accesses locals much faster than globals, using map and list comprehensions where possible, and use xrange for ranges.


#!/opt/local/bin/python
""" The purpose of this script is to explore the cost of expressing
things in an object oriented way.
"""
from time import time
import Numeric
import math
n = 20
class Nlist:
def __init__(self):
self.nip = 0
self.iap = Numeric.zeros((n*n,2))
def build(self):
for i in range(n):
for j in range(i+1,n):
self.iap[self.nip,0] = i
self.iap[self.nip,1] = j
self.nip += 1
def traverse(self):
for k in range(nl.nip):
i = nl.iap[k,0]
j = nl.iap[k,1]
class Force:
def __init__(self,nl):
self.nl = nl
def pairwise(self,i,j):
g = math.sqrt(i*j)
def apply(self):
for k in range(nl.nip):
i = nl.iap[k,0]
j = nl.iap[k,1]
self.pairwise(i,j)
def traverse(nl):
for k in range(nl.nip):
i = nl.iap[k,0]
j = nl.iap[k,1]
def apply_force(f,nl):
for k in range(nl.nip):
i = nl.iap[k,0]
j = nl.iap[k,1]
f.pairwise(i,j)
nl = Nlist()
nl.build()
print "Timing externel function for nl traversal versus object method"
start_time = time()
nl.traverse()
end_time = time()
t1 = end_time - start_time
print "object method: %f elapsed" %(t1)
start_time = time()
traverse(nl)
end_time = time()
t2 = end_time - start_time
print "external function: %f elapsed" %(t2)
diff = (t1-t2)/t1
print "Diff as fraction of t1 %f" %(diff)
print "Timing force object with (same) attached neighbour lists versus seperate neighbour list"
bigforce = Force(nl)
smallforce = Force(nl)
start_time = time()
bigforce.apply()
end_time = time()
t1 = end_time - start_time
print "internal force loop: %f elapsed" %(t1)
start_time = time()
apply_force(bigforce,nl)
end_time = time()
t2 = end_time - start_time
print "external force loop: %f elapsed" %(t2)
diff = (t1-t2)/t1
print "Diff as fraction of t1 %f" %(diff)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: