Sometimes I have to check for some condition that doesn't change inside a loop, this means that the test is evaluated in every iteration, but I think this isn't the right way.
I thought since the condition doesn't change inside the loop I should only test it only once outside the loop, but then I will have to "repeat myself" and possibly write the same loop more than once. Here's a code showing what I mean:
#!/usr/bin/python
x = True #this won't be modified inside the loop
n = 10000000
def inside():
for a in xrange(n):
if x: #test is evaluated n times
pass
else:
pass
def outside():
if x: #test is evaluated only once
for a in xrange(n):
pass
else:
for a in xrange(n):
pass
if __name__ == '__main__':
outside()
inside()
Running cProfile
on the previous code gave the following output:
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.542 0.542 0.542 0.542 testloop.py:5(inside)
1 0.261 0.261 0.261 0.261 testloop.py:12(outside)
1 0.000 0.000 0.803 0.803 testloop.py:3(<module>)
This shows that obviously, testing once outside the loop gives better performance, but I had to write the same loop twice (maybe more if there were some elif
s).
I know that this performance won't matter in most cases, but I need to know what's the best way to write this kind of code. For example is there a way to tell python to only evaluate the test once ?
Any help is appreciated, thanks.
Actually after making some tests, I'm now convinced that the difference in performance is mainly affected by other code performed within the loops, not by the evaluation of tests. So for now I'm sticking with the first form, which is more readable, and better for debugging later.
First, a major component of the performance difference between your examples is the time it takes to lookup a global. If we capture it into a local variable:
def inside_local():
local_x = x
for a in xrange(n):
if local_x:
pass
else:
pass
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.258 0.258 0.258 0.258 testloop.py:13(outside)
1 0.314 0.314 0.314 0.314 testloop.py:21(inside_local)
1 0.421 0.421 0.421 0.421 testloop.py:6(inside)
most of the performance difference disappears.
In general whenever you have common code you should try to encapsulate it. If the branches of the if
have nothing in common apart from the loop then try to encapsulate the loop iterator e.g. into a generator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With