massive if's vs continue

Started by
22 comments, last by Steadtler 15 years, 11 months ago
Quote:Original post by nb
Quote:Original post by samoth
Quote:Original post by nb
some compilers / compiler settings actually do the if statements either way you suggested. They can be set so that they stop checking once a section of the if statement fails, or they can check the entire expression regardless of if any have already failed or not.
Careful, though. This may be a "speciality" of Delphi (don't know anything about Delphi really).
In C/C++, it is clearly against the standard (you are guaranteed that the right side of && will not execute if the left side is false), and any compiler implementing it differently is broken. I wouldn't want to use a compiler that lets you customize such a behaviour, since many programmers indeed rely on that assertion, so it may break code that's perfectly valid.


i did say 'some'. as a default delphi does not check all statements if one is already found to be crap. i'm sure there's a reason why it can be toggled to do checks on all statements... maybe something to do with being able to debug values that it would otherwise trash and render un-readable when it got to the if statement. don't know. all's good.


As samoth says, any C/C++ compiler that doesn't short-circuit logic is very broken. It breaks a very common if statement involving NULL pointers:
if(ptr && *ptr == some_value)...
If the compiler doesn't short-circuit the &&, it will dereference a null pointer.
Advertisement
I wouldn't worry about optimizations like this, at this level. The important thing is that the code works correctly, is readable, and maintainable. The time to deal with these kinds of micro optimizations is when a profiler suggests that it's a bottleneck. If this is for a game, the cost of several if's is negligible compared to the graphics operations.

Premature optimization *rarely* works to your advantage.
Quote:Original post by Hodgman
Quote:Original post by Driv3MeFar
The same happens with the statements in the first if block you posted. Logical operators like && can be short-circuited, such that when it becomes impossible for the whole statement to be true, the rest of it is not evaluated.


^^^ Take this knowledge on board if you do want to optimise your if statements at some point.

Lets say for example that Test#1("player.IsAlive()" ) takes 5ms to execute, but Test#2("player.GetTeam() == 2") and Test#3("player.GetHP() < 75") only take 1ms. (I know these numbers are absurd, it's just for illustrative purposes!)

To make your loop as fast as possible, you should order these tests within the if from fastest to slowest. i.e.:
if( player.GetTeam() == 2 && // 1ms    player.GetHP() < 75   && // 1ms    player.IsAlive()       ) // 5ms


This way, if either of the fast tests return false, then the slow test will never be executed. I.e. it only calls the 5ms function on objects which have passes the quick tests.


Actually, you could do better. You could try to predict the probabilities of each test's failure. Assume ti is the time of the ith condition and pi is it's probability of failure. The first takes average time of t1. The second (1 - p1) * t2. The third (1 - p1) * (1 - p2) * t3. This is true under the assumption that p1, p2 and p3 are uncorrelated. So the total average time is: t1 + (1 - p1) * t2 + (1 - p1) * (1 - p2) * t3. So you have to put your conditions in such order that the total time is minimized.
It looks like you are starting down the path of an AI.

As a side note, you might look in to using a more extensible state-based AI system: http://wiki.gamedev.net/index.php/State_machine
Quote:Original post by Rydinare
Having said all that, on a slight tangent, if it's common case that you're checking for the same few conditions in multiple areas, I would advise making a utility function that checks all three, to keep the client code a bit cleaner.

Something like:

*** Source Snippet Removed ***

I do think that may help readability. If the condition is only tested in one place in the code, then this may be overkill.

I agree. One might even consider to keep the check inside the class itself. YMMV of course.

void Player::heal(){    strength = 100;}void Player::update(){    if (shouldHeal())    {        heal();        raisePlayerHealedEvent(this);        /* or something... */    }}bool Player::shouldHeal(){    return (IsAlive() && ..);}
3 ifs are not *massive*. 250kb source code of thousands of ifs is massive.

If you're using OO, one alternative is this:
for (Healer h : healers) {  player->HealPartyMembers();}...bool Player::NeedsHealing(){  return alive && health < 75;}void Healer::HealPartyMembers(){  for (Player p : my_team) {    if (p->needsHealing()) Heal(p);  }};


The team is implicit property of a player, NeedsHealing accesses local variables, Healer (also player) may be a function limited to Healer <- Player if using OO, whereas NeedsHealing is common to all players.

The above can be done in OO, functional or procedural manner.
As has been noted, this is no good as an optimisation. If nothing else, the compiler is likely to implement the two possibilities in the same way anyway. As a point of coding style, though, I prefer the continues as being easier to read, because the lines are shorter and there is one logical statement per line. No need to parse logical operators or parens, in cases where that's significant. This is a question of taste, though. Continues are also nice for avoiding nesting that pushes your whole code to the right.
To win one hundred victories in one hundred battles is not the acme of skill. To subdue the enemy without fighting is the acme of skill.
Quote:Original post by Hodgman
Quote:Original post by Driv3MeFar
The same happens with the statements in the first if block you posted. Logical operators like && can be short-circuited, such that when it becomes impossible for the whole statement to be true, the rest of it is not evaluated.


^^^ Take this knowledge on board if you do want to optimise your if statements at some point.

Lets say for example that Test#1("player.IsAlive()" ) takes 5ms to execute, but Test#2("player.GetTeam() == 2") and Test#3("player.GetHP() < 75") only take 1ms. (I know these numbers are absurd, it's just for illustrative purposes!)

To make your loop as fast as possible, you should order these tests within the if from fastest to slowest. i.e.:
if( player.GetTeam() == 2 && // 1ms    player.GetHP() < 75   && // 1ms    player.IsAlive()       ) // 5ms


This way, if either of the fast tests return false, then the slow test will never be executed. I.e. it only calls the 5ms function on objects which have passes the quick tests.

For a real-world example, physics engines often use bounding-spheres to check for possible collisions *before* doing a real collision test. This is because a sphere-test is really quick, but a real collision test is really slow.


Actually its not always the best idea to sort the checks in the order fastest to slowest, you also should consider the fail/success rates, a check that fails 99% of the time is a better choice for the first check than one that is 50% faster but only fails 20% of the time.
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Quote:Original post by WanMaster
Quote:Original post by Rydinare
Having said all that, on a slight tangent, if it's common case that you're checking for the same few conditions in multiple areas, I would advise making a utility function that checks all three, to keep the client code a bit cleaner.

Something like:

*** Source Snippet Removed ***

I do think that may help readability. If the condition is only tested in one place in the code, then this may be overkill.

I agree. One might even consider to keep the check inside the class itself. YMMV of course.

*** Source Snippet Removed ***


I thought about putting it in the Player class when I first mentioned it as an example. My main reason for not suggesting such is the open-closed principle. Sounds like all of the internals could be accomplished through the public interface of the Player class, and therefore the Player class doesn't really to take the hit of having utility sorts of modifications. Of course, this is at the risk of promoting feature envy; an interesting trade-off.
Quote:Original post by SimonForsman
Actually its not always the best idea to sort the checks in the order fastest to slowest, you also should consider the fail/success rates, a check that fails 99% of the time is a better choice for the first check than one that is 50% faster but only fails 20% of the time.

Yeah, rozz666 already pointed that out ;)

Quote:Original post by frob
It looks like you are writing a letter!

Sorry, couldn't help it ^^

This topic is closed to new replies.

Advertisement