The short answer is, yes it will if you compare it to not having an if
statement there at all. But that's not the answer you want.
Your program is compiled and is run on a CPU. You do not mention which CPU architecture your project targets, but most modern CPUs use branch prediction, which helps speed-up cases of naive condition checking when the condition may vary at runtime.
Even if in your case, as you say, the choice variable will not change during the program lifetime, without branch prediction a compiler and the CPU wouldn't have much of a chance to avoid checking it every time you instruct so in your source code. It's a run-time variable and with C++ there are no constructs in place to help the compiler or the CPU know when it will change. They may use clever heuristics -- like substitute a number of if
statements with a call dispatch (a function call), but that would be the same if you did the following:
arma::vec (*chosen_acceleration_proc)(arma::vec, double);
arma::vec acceleration(arma::vec u, double t)
{
return chosen_acceleration_proc(u, t);
}
where you could switch chosen procedure at runtime:
chosen_acceleration_proc = acceleration_1; /// For example
The above will prevent the compiler from inlining your different acceleration procedures that it otherwise could. However, the only additional cost of your acceleration
procedure is invoking chosen_acceleration_proc
. The cost of completing the chosen acceleration procedure may far outweigh the cost of invoking it. The bottomline here is that if the acceleration procedures could benefit from inlining, using procedure pointers is an ill-fit approach.
You'd have to profile your compiled program to see if the cost of such dispatch is negligible or significant.
Back to branch prediction, for a CPU with branch-prediction, the CPU will "learn" after making the same choice certain number of times:
arma::vec acceleration(arma::vec u, double t) {
switch(acceleration_proc_id) {
case 1: return acceleration_1(u, t);
case 2: return acceleration_2(u, t);
/// etc
}
}
...meaning that when you set the acceleration_proc_id
to some chosen value, after some time, the CPU will just assume that it's going to stay that and prefetch and speculatively execute instructions as if that choice was a given. See this most excellent answer for more about branch prediction and how it helps or fails.
A switch
is almost always a better choice than using if
and else
, it certainly is in your case. That's partially because one may only use constant expressions to switch on, which is useful for optimization to the compiler. There are other reasons.
What if the different procedures are all members of a class?
You can still use pointers -- to member procedures -- if the different acceleration computing procedures belong to the same class or derivatives of it. If they're all different members with identical signature, in the same class:
class Foo {
arma::vec acceleration1(arma::vec u, double t);
arma::vec acceleration2(arma::vec u, double t);
arma::vec acceleration3(arma::vec u, double t);
}
arma::vec (Foo::*chosen_acceleration_proc)(arma::vec u, double t);
Foo obj;
chosen_acceleration_proc = &Foo::acceleration2; /// Specify your preferred procedure
arma::vec acceleration(arma::vec, double t) {
return (obj.*chosen_acceleration_proc)(u, t);
}
Regardless of how you design your application -- whether it's an "application" object that contains the different acceleration procedures, or some other semantics apply -- you can just point a pointer like above to the desired procedure, this isn't really any different than with "ordinary" procedures.
To conclude, the best code a CPU can run is no code at all -- if you need to switch at runtime or call actual, non-inlined, procedures, the cost will be there, as opposed to using pre-processor or constant expressions to decide which acceleration function you want as the program is compiled.
You ultimately need to profile your program that uses switch
vs. one that uses procedure pointers, or whatever else method you can conjure that I haven't covered. Profiling will determine where CPU spends most of its time anyway. If after profiling you conclude that the cost of being able to decide on acceleration procedure once at run-time, is negligible compared to actually computing acceleration, then just write the most readable, shortest, and simplest code you can write and leave the rest to the compiler -- that's its job.