I have encountered a strange performance problem when adding to an str class member in python 2.7.3. I know that accessing local variables are faster, however, in the problem below, there is more than 100x speed difference between the two loops. The one that accesses a.accum_ starts fast but slows down, as if str iadd is O(n^2) with the length of str.
Does anyone know the reason?
# Fast ( < 1sec):
accum = str()
for ii in range(1000000):
if (ii % 10000) == 0:
print 'fast cnt', ii
accum += 'zzzzz\n'
# Much slower ( > 5 mins):
class Foo:
pass
a = Foo()
a.accum_ = str()
for ii in range(1000000):
if (ii % 10000) == 0:
print 'slow cnt', ii
a.accum_ += 'zzzzz\n'
Python strings are immutable and so can't have an
__iadd__
method. What you are witnessing in the first example is a micro optimisation of the CPython interpreter. In the first example the interpreter has noticed that it has a local variable that has a reference count of 1. Thus, the interpreter can cheekily get away with modifying the string in place. Even though this violates the contract ofstr
, at no point during the execution of the program will it be apparent that this contract was briefly violated.In the latter example this micro optimisation isn't being implemented, which is why it's so slow. It looks like the optimisation could be applied, so I'm not sure why it isn't being applied.
Generally though, if building a string, collate the substrings in a list and then use
str.join
to create the end product.