massive if's vs continue

Started by
22 comments, last by Steadtler 15 years, 11 months ago
Hey all, Just wondering what's faster between using huge if statements, vs using continues. Here's an example for healing all the living players on team 2 with less than 75 hp: //Using large if statements for(int i=0; i < player.size(); i++) { if(player.IsAlive() && player.GetTeam() == 2 && player.GetHP() < 75) player.Heal(); } //OR using continues for(int i=0; i < player.size(); i++) { if(!player.IsAlive()) continue; if(player.GetTeam() != 2) continue; if(player.GetHP() >= 75) continue; player.Heal(); } Are the continues faster, because they can stop checking once a criteria is not met? Or are they slower because they require more if calls or something.. If someone could explain to me (or point me to a useful article) about how this works, I'd greatly appreciate it. Thanks
--------------------------Check out my free, top-down multiplayer shooter: Subvein
Advertisement
Quote:Original post by bencelot
Are the continues faster, because they can stop checking once a criteria is not met?


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.

The two code blocks you posted will be near as makes no difference the same speed, so choose the one that is the easiest to read an makes the most sense. Even if they are slightly different, it's likely not worth optimizing because it will not be a bottleneck. In general, profile to determine your slowest code, then fix it at an algorithmic level, not with hackery like this.
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. Look into your settings and you'll find it i'm sure. You are trying to enforce a situation on the compiler it already caters for.

In Delphi it is referred to in the project options as 'complete boolean eval' which is unset, which is saying that it does NOT check each item in an 'if' statement if it already comes across a section that will fail the test.

It's most likely already set correctly. So just code whichever way is more productive for you. Use the if's if that serves you best, or the continues. Sometimes it can be more about layout and being readable than being 'fast', but even then you can span the if expression over multiple lines anyway.
Hi bencelot,

optimizing is an art.

as Driv3MeFar stated is correct.

but... if you want to gain performance sometimes restructing what you are doing is what you want to do. The best optimization is actually doing nothing at all.

I can see you have a value for HP and a flag for alive... why not combine them. Also having more then one list could really improve your performance no need having dead players in a list..move them out of the list and into another.

I can see that the for-next is handling healing and only applies them to players that should get healing... you could create a list that contains references to players that are valid for healing. Then you for-next would look like this:

for(int i=0; i < player.size(); i++) {
playerHealable.Heal();
}

This way you will only have to add/remove players from that list when something has changed that would cause them to be added or removed.

hope this helps,
Peter Wraae Marino


http://osghelp.com - great place to get OpenScenGraph help
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.
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.
The if statement is definitely better. As others have said, this is not a performance issue at all, but one of coding style. The reason why the if statement is better is that it's easier to refactor. Let's say that you need to do something else besides healing. You can just put another statement into the loop, but with continues like that, you'd need another loop. Also, it's somewhat less typing.

If you think that the statement is too wide, you can always do this:
if (  player.IsAlive()   && player.GetTeam() == 2   && player.GetHP() < 75)   player.Heal();

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.
Assuming that your GetXXX() IsXXX() functions are typical inline const accessors, which they probably are, you might even consider having them all evaluated and using & instead of &&. But stop! Read to the end before thinking about that.

Since & is not the same thing as &&, a lot of care has to be taken. In the above example, IsAlive() must return bool, else using & will break your program. Generally, you should never think about such optimisations unless it's really necessary. They make your code more error-prone, which isn't good. Still, this is FYI.

The reasoning behind using & is that if the getter functions are mere accessors, the branch instructions generated by either && or continue will by far be the most expensive thing in this situation, especially if a branch is mispredicted.
& will generate an "and" instruction which is fast and can't mispredict. However, & should only ever be used if everything is bool and has no side effects (and always with great caution), since otherwise you may have very hard to track bugs.
For example, if IsAlive() just returns the hp value of the player in an int, then if(IsAlive()) will work just fine, but if(GetGroup() == 5 & IsAlive()) will not work correctly. The worst thing is, it will actually work in 50%, which makes it such a pain to track down...
I agree with the sentiment of using the if statement, rather than multiple if statements with continues. I can safely say that in my many years of programming, I've never found a case where I needed to use continue. I also would say that the majority of developers I know don't use continue often. So, there's certainly the factor that code with continue might take a little longer to get the grasp of for someone else.

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:

bool needsToBeHealed(const Player& p_player, TeamId p_teamId, HitPoints p_minHitPointThreshhold){    return p_player.IsAlive() && p_player.GetTeam() == p_teamId && player.GetHP() < p_minHitPointThreshhold;}// client codeif (needsToBeHealed(player, 2, 75)){   player.Heal();}


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

This topic is closed to new replies.

Advertisement