2

Please pardon me if this is a duplicate question. I have two nested for loops which will iteration for around mn times (the complexity is around 3k).

Inside these for loops, I have 3 If conditions based on what I do certain operations. I am trying to see if there are any ways to avoid these if conditions inside the for loop as these will be executed for mn times.

Below is the skeletal of existing implementation:

var conditionA = <set conditions for A>
var conditionB = <set conditions for B>
var conditionC = <set conditions for C>

foreach (var x in X)
{
  foreach (var y in Y)
  {
    if (conditionA)
    {
      //computeA ..makes use of x and y
    }
    if (conditionB)
    {
      //computeB..makes use of x and y
    }
    if (conditionC)
    {
      //computeC..makes use of x and y
    }
  } //end of inner foreach
} 

As you can see that all the 3 conditions are determined before foreach, is there any way I can get rid of redundant IF statements at each iteration?

Laiv
  • 14,283
  • 1
  • 31
  • 69
  • 3
    Possible duplicate of [Is micro-optimisation important when coding?](https://softwareengineering.stackexchange.com/questions/99445/is-micro-optimisation-important-when-coding) – gnat Mar 22 '18 at 06:53
  • 3
    Have you actually determined that this is a performance problem? Until you have, don't worry about it. – Philip Kendall Mar 22 '18 at 07:04
  • Depends on the conditions. If they depend on where you are in the loop, seriously consider revising your loop(s). If all three code blocks are the same, you need only "or" the conditions together. If you need all combinations of x and y, looping this way will consider x, y and y, x different iterations (maybe that isn't what you mean to do). – Neil Mar 22 '18 at 07:11
  • 2
    The question is. Is there something to improve? I mean, what is bugging you? Efficiency? Readability? Maintainability? Like in CAP Theorem, usually it's a matter of trade-off. What would you like to improve and what are you willing to sacrifice? – Laiv Mar 22 '18 at 07:44
  • @gnat: The question isn't specifically about micro-optimization, even though you've made that judgment about it. – Robert Harvey Mar 22 '18 at 13:39
  • @RobertHarvey have you read it? "avoid these if conditions inside the for loop as these will be executed for mn times" – gnat Mar 22 '18 at 14:09
  • Depending on your language, a compiler might be able to optimize this, even if it knows nothing about what's inside `computeA`, `computeB`, `computeC`. I wouldn't rely on this, but it suggests that you are optimizing prematurely. Take a look at a C++ and gcc example: https://godbolt.org/z/8oOwaW You have 3 conditions, so there are 8 ways in which the inner loop could run. Gcc generates code for each of these 8 ways and runs the inner loop without checking the conditions again. – pschill Jul 11 '19 at 08:23

4 Answers4

9

One way to avoid those ifs inside the loops is to put them outside, by deciding which functions to call in advance:

//set the values of conditionA, conditionB and conditionC;

functionA = conditionA ? computeA : noOp
functionB = conditionB ? computeB : noOp
functionC = conditionC ? computeC : noOp

foreach (var x in X)
{
    foreach (var y in Y)
    {
        functionA(x,y)
        functionB(x,y)
        functionC(x,y)
    }
}

Of course, there's no guarantee this will be faster, but it "meets the brief".

Alternatively, if you wish to only call functions if needed, create a set of functions to call in advance and loop through them each time:

//set the values of conditionA, conditionB and conditionC;

Collection functions;
if (conditionA) functions += computeA
if (conditionB) functions += computeB
if (conditionC) functions += computeC

foreach (var x in X)
{
    foreach (var y in Y)
    {
        foreach (var f in functions)
        {
            f(x,y)
        }
    }
}

This approach runs the risk of being slower though as you are adding the creation of an iterator for the functions nm times to the process. Of course, you are avoiding making unnecessary function calls and avoiding unnecessary checks. So in some situations, it may even be faster.

Robbie Dee
  • 9,717
  • 2
  • 23
  • 53
David Arno
  • 38,972
  • 9
  • 88
  • 121
  • By functions as first class, you mean lambadas? (Sorry, I'm not too familiar with that kind of programming/paradigm). – Laiv Mar 22 '18 at 07:49
  • 1
    @Laiv, in C#, they are called delegates. I think Java refers to them as lambdas. I've always known them as function pointers as I first used them in C. – David Arno Mar 22 '18 at 08:00
  • "pretty much guaranteed to be slower" - don't make such unjustfied assumptions. We don't know how `conditionA/B/C` look in the real code, if they are time consuming, your code might be faster. – Doc Brown Mar 22 '18 at 12:05
  • @DocBrown, yep it might be, thus the "pretty much" ;). Edited though to make it less assuming. – David Arno Mar 22 '18 at 12:09
3

There are many ways. Here's a tidy one:

if (conditionA)
{
    foreach (var x in X)
    {
        foreach (var y in Y)
        {
            //computeA ..makes use of x and y
        }
    }
}

if (conditionB)
{
    foreach (var x in X)
    {
        foreach (var y in Y)
        {
            //computeB ..makes use of x and y
        }
    }
}

if (conditionC)
{
    foreach (var x in X)
    {
        foreach (var y in Y)
        {
            //computeC ..makes use of x and y
        }
    }
}

This assumes there are no dependencies, side effects, or otherwise reasons to care about order of execution of the computations or conditions.

If you're thinking that's inefficient I'll point out that your original is O(n2) while this one is O(3n2) which is the same complexity under Big O. Don't let thoughts of efficiency trap you in hard to read code.

I did promise tidy so here it is:

if (conditionA)
{
    A a = computeA(X, Y);
}
if (conditionB)
{
    B b = computeB(X, Y);
}
if (conditionC)
{
    C c = computeC(X, Y);
}

The cool thing about that is it makes the fact that you have an initialization problem really obvious.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • In such situations I really miss functional programming. I could have created a function with signature (X,Y,bool,function foo). Inside depending on bool is true or false calls foo by passing X and Y. – Manoj R Mar 22 '18 at 07:20
  • The problem I see is that now (in the worse case) we iterate 3 times over the same matrix. The problem (if any) has been replaced by another one (regardless this one makes things more readable). However, 2/3 solutions here are still verbose and face the same problems of maintainability (if that matters) – Laiv Mar 22 '18 at 07:35
  • 2
    @laiv when Big O doesn't care I don't care until performance tests make me care. If it's readable I can make it as fast as I need to. – candied_orange Mar 22 '18 at 07:36
  • That's why I said 2/3. The last one at least makes things easier to read and comprehensible (this at least a good improvement). Just wanted to point to the fact that sometimes, no matter how you look at it, the improvement is going to be negligible or inexistent. – Laiv Mar 22 '18 at 07:38
  • 1
    If order of execution matter this solution will give different results then the original. – Pieter B Mar 22 '18 at 13:54
  • @PieterB better? – candied_orange Mar 22 '18 at 14:00
  • What I mean is, let's say that computeA prints an A and computeB a B and computeC a C, and both X an Y hold {1,2}, the original will print: ABCABCABCABC, but your solution will print AAAABBBBCCCC – Pieter B Mar 22 '18 at 14:06
  • @PieterB and now? – candied_orange Mar 22 '18 at 14:12
0

You should use function instead of using multiple conditions. It will surely improve your efficiency as well as execution time.

Ishan Shah
  • 339
  • 1
  • 9
0

You can just accept it as it is. The code is clear enough as it is. None of the proposed changes seems better to me in a meaningful way.

I’d ask myself what is the reason why you want to get rid of the ifs. As it is now, maintenance cost is very low, readability is good, performance is good.

gnasher729
  • 42,090
  • 4
  • 59
  • 119