Testing a condition that does not change inside the loop

Sometimes I need to check some condition that does not change inside the loop, this means that the test is evaluated at each iteration, but I think this is the wrong way.

I thought, since the condition does not change inside the loop, I should check it only once outside the loop, but then I will have to "repeat myself" and, possibly, write the same loop more than once. Here is the 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 in the previous code gave the following result:

  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 should have written the same loop twice (maybe more if there were several elif s).

I know that this performance will not matter much in most cases, but I need to know what is the best way to write such code. For example, is there a way to tell python to check the test only once?

Any help is appreciated, thanks.

EDIT:

Actually, after some tests, I am now convinced that the difference in performance mainly depends on other code executed in the cycles, and not on the evaluation of the tests. Therefore, at the moment I stick to the first form, which is more readable and better debugged later.

+6
source share
7 answers

First, the main component of the performance difference between your examples is the time it takes to find the global one. If we write it to 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 a common code, you should try to encapsulate it. If if branches have nothing in common except a loop, try encapsulating a loop iterator, for example. into the generator.

+5
source

This is what I usually do in this situation.

 def inside(): def x_true(a): pass def x_false(a): pass if x: fn = x_true else: fn = x_false for a in xrange(n): fn(a) 
+5
source

python has functions such as closures, lambda functions, gives first-class status to functions and many built-in functions that really help us remove duplicate code, for example, imagine that you need to apply a function to a sequence of values, you can do it this way

 def outside(): if x: # x is a flag or it could the function itself, or ... fun = sum # calc the sum, using pythons, sum function else: fun = lambda values: sum(values)/float(len(values)) # calc avg using our own function result = fun(xrange(101)) 

If you give us the exact scenario, we can help you optimize it.

+3
source

I donโ€™t know which interpreted language provides support in this direction, compiled languages โ€‹โ€‹will most likely make a comparison only once (optimization of the invariant cycle), but this will help little if the estimate of x is simple. Obviously, the code, which should be instead of pass statements, cannot be completely identical, since "if" would not be used then. You can usually write a procedure named in both places.

+2
source
 def outside(): def true_fn(a): pass def false_fn(a): pass fn = true_fn if x else false_fn for a in xrange(n): fn(a) 
+1
source

In your case, it depends on what you want: readability or performance.

If the task you are performing is some kind of filter, you can also use list_comprehension to start the loop:

 [e for e in xrange(n) if x] 

If you show a little more of your code, I could suggest something.

0
source

Based on your initial question, that you want to check the value of x without spending a lot of system resources, you have already accepted the answer related to copying the value of the global variable x to a local variable.

Now, if a multi-step function is involved in returning the value of x, but you are guaranteed that the result will always be the same for x, then I would consider the possibility of memoizing the function. Here is a very good stackoverflow link on the topic

0
source

Source: https://habr.com/ru/post/918538/


All Articles