Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing a condition that doesn't change inside a loop

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 elifs).

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.

EDIT:

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.

like image 646
Amr Avatar asked Jun 20 '12 08:06

Amr


1 Answers

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.

like image 116
ecatmur Avatar answered Sep 28 '22 19:09

ecatmur