1

Suppose that I'm using C++. Now I have the following code:

int flag;
// ...
while (!TimeToExitLoop()) {
    Func();
}

The while loop will be executed a huge number of times, and Func is a time-critical function like this:

void Func() {
    // some big stuff ...
    if (flag > 1) {
        // logic 1 ...
    }
    else {
        // logic 2 ...
    }
}

Also, the value of flag won't be change within the while loop. Therefore, it is better to move the conditional if statement out of the while loop, and define two separate functions for the two conditions like this:

int flag;
// ...
if (flag > 1) {
    while (!TimeToExitLoop()) {
        Func_FlagBiggerThanOne();
    }
}
else {
    while (!TimeToExitLoop()) {
        Func_FlagNoBiggerThanOne();
    }
}

However, that will result in the repetition of the "big stuff" in Func by Func_FlagBiggerThanOne and Func_FlagNoBiggerThanOne:

void Func_FlagBiggerThanOne() {
    // big stuff ...
    // logic 1 ...
}

void Func_FlagNoBiggerThanOne() {
    // big stuff ...
    // logic 2 ...
}

which will violate the Don't-Repeat-Yourself principle. We can not put that "big stuff" in some function because invoking that function will be more time consuming than the original if statement. One of the solutions is to define a macro for that big stuff, but what if "logic 1" and "logic 2" will use the variables defined in "big stuff"? Though macro still works, that may result in ugly code, the programme's reader might think, "where the heck are those variables defined?"

leppie
  • 115,091
  • 17
  • 196
  • 297
SSHuv
  • 65
  • 11
  • use a compiler/linker which can unswitch the conditional out of the loop... but that compiler/linker will have to be quite funky if `Func` is in another file, because `flag` is apparently global. – lijie Nov 24 '10 at 11:01
  • 1
    « We can not put that "big stuff" in some function because invoking that function will be more time consuming than the original if statement. » Can't you inline it? – Zecc Nov 24 '10 at 11:23
  • I think a good programmer should not assume anything from the compiler/linker. Also, Zecc, I guess inline won't work because that stuff is very "big". – SSHuv Nov 25 '10 at 05:11

2 Answers2

0

We can not put that "big stuff" in some function because invoking that function will be more time consuming than the original if statement.

I have a favorite Old English idiom: "penny wise pound foolish" (of which I've been guilty). Another way to put it is "don't sweat the small stuff". I wish I could find the original quote on SO: "getting a haircut to lose weight".

Make the code clean, and if you can reasonably follow DRY, do so. Then do your performance tuning. This is how I do it. I would be very surprised if the cost of calling that function, or the cost of the IF statement, even rises onto the radar.

Community
  • 1
  • 1
Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
  • Thanks, Mike. I have just read your "This is how I do it" link. Actually in my project I was following the similar steps you suggested. Now I seem to have hit a wall. The cost of calling a function in `Func` is indeed time-consuming according to my experiment. I have not tested the effect of moving the IF out of `Func` yet. Now I just leave it there to keep the code clean. But I'm curious about what to do if I want to go to the extreme. Suppose that the WHILE loop are executed 10^10 times, then the IF statement will cost about 5 seconds on a 2GHz CPU. – SSHuv Nov 25 '10 at 05:49
  • 1
    Oh...if the IF statement takes 5 seconds in all, the "big stuff" will take much more time. So it's really not worthy to spend a lot of time moving the IF out of `Func`. The only reason to do that is perfectionism and curiosity. – SSHuv Nov 25 '10 at 07:14
  • @Zhixiang: I think that's right. It's better to think in relative terms, rather than absolute. When you've tuned the "big stuff" so well that it's not big any more, and the IF and `Func` take a large enough percent to matter, then your code is "faster than a scalded dog" and you are an awesome coder :-) – Mike Dunlavey Nov 25 '10 at 14:46
  • 1
    @Zhixiang: If you've really hit that point, and the IF and/or `Func` are consuming 10% or more, and you are calling them trillions of times, so you actually *need* to squeeze more cycles, then by all means, dump DRY overboard, because all it does is make maintenance easier. That's when I've been known to use a macro to keep DRY, if I didn't want to trust the compiler to inline it. That's also when I'd wonder if I should take a different approach, like precompiling. – Mike Dunlavey Nov 25 '10 at 14:58
0

I just think of a solution. The main idea is to use #ifdef:

#define FUNC()  do {  \
    // big stuff ...
#ifdef FLAG_BIGGER_THAN_ONE  \
    // logic 1 ...  \
#else  \
    // logic 2 ...  \
#endif  \
} while(0)

So we can write the main logic like this:

int flag;            
// ...            
if (flag > 1) {            
    while (!TimeToExitLoop()) {
#define FLAG_BIGGER_THAN_ONE            
        FUNC();
#undef FLAG_BIGGER_THAN_ONE            
    }            
}            
else {            
    while (!TimeToExitLoop()) {            
        FUNC();            
    }            
}

However, just as Mike suggested, "it's better to think in relative terms, rather than absolute. If the "big stuff" can not be tuned, such an optimisation will be useless.

SSHuv
  • 65
  • 11