Extremely slow object instantiation in Python 2.7

1.1k views Asked by At

I recently had to complete an assignment that used a lot of coordinate operations. Thinking to save time and simplify my code, I defined a class to encapsulate the behaviour of a coordinate pair. The class looked like this:

class Vector (tuple) :
    def __init__ (self, value) :
        tuple.__init__ (self, value)


    def __add__ (self, other) :
        return Vector ((self [0] + other [0], self [1] + other [1]))

This allowed me to write code like this (for example):

def translate (pointList, displacement) :
    return [point + displacement for point in pointList]

But my application was terribly slow. Much slower than other assignments. I couldn't locate any inefficiency in my implementation of the algorithm, so I did a simple test to see what the overhead was of the Vector class. I expected somewhere between 5% and 15%.

My test of the Vector class looked like this:

v = Vector ((0, 0))
d = Vector ((1, -1))
loopIdx = 3000000
while loopIdx > 0 :
    v = v + d
    loopIdx -= 1
print (v)

This runs (typically) in this kind of time:

real    0m8.440s
user    0m8.367s
sys     0m0.016s

For comparison I ran this code:

v = (0, 0)
dX = 1
dY = -1
loopIdx = 3000000
while loopIdx > 0 :
    v = ( v [0] + dX, v [1] + dY )
    loopIdx -= 1
print (v)

Run time for this code is:

real    0m1.004s
user    0m0.995s
sys     0m0.006s

Have I done something seriously wrong, or does using class objects in Python really mean your application will take over 8 times as long to run?

1

There are 1 answers

1
tobias_k On BEST ANSWER

Not really an answer how to make you class faster, but more of an alternative.

Instead of subclassing tuple and writing all those add, sub etc. methods yourself, you cold just use Python's bultin complex number type for 2D coordinates, which has all those operations already built in, correct and and super-fast.

>>> %timeit vector_no_init()
1 loops, best of 3: 1.39 s per loop
>>> %timeit plain_tuple()
1 loops, best of 3: 403 ms per loop
>>> %timeit complex_nums()
1 loops, best of 3: 171 ms per loop

For rotation, you can use complex multiplication: Just multiply your complex coordinate with a complex number that has, in polar form, absolute value 1 and a phase equal to the angle you want to rotate by. To rotate by 90 degrees, you can just multiply by 1j (anti-clockwise) or -1j (clockwise). For all other angles, use the cmath module for translation to and from polar form.

>>> c = complex(4, 2)
>>> c * cmath.rect(1, math.radians(45))
(1.4142135623730954+4.242640687119285j)

However, I would suggest not to subclass complex to make rotate a method of that class, because in this case you will have to overwrite all the other methods, like add, as well, otherwise the result of addition will be a regular complex number, not providing the rotate method. And this would undo all those performance gains, making it just as slow as your Vector class. Instead, just make a function rotate(complex, angle) -> complex.