简体   繁体   English

条件不变的 if 语句会减慢我的 C++ 代码的速度吗?

[英]Will an if statement with an unchanging condition slow down my C++ code?

I am solving a system of coupled ordinary differential equations, and between runs of my program I wish to change which function to integrate.我正在求解一个耦合常微分方程组,并且在我的程序运行之间,我希望更改要积分的函数。 As of now, I simply comment and un-comment three return statements to toggle between the different functions, like this:到目前为止,我只是简单地注释和取消注释三个 return 语句来在不同的函数之间切换,如下所示:

arma::vec acceleration(arma::vec u, double t)
{   /*
    The current acceleration of the system.
    */

    // return acceleration_1(u);
    // return acceleration_2(u);
    return acceleration_3(u);
}

acceleration is called many times, and I would like to make the code efficient, but I would also like to change which function to integrate with an input parameter, and not by entering the depths of the code to comment and un-comment different statements. acceleration被多次调用,我想让代码高效,但我也想改变与输入参数集成的函数,而不是通过输入代码的深度来注释和取消注释不同的语句。

If I were to send in a parameter, say choice , which can only be one of {1, 2, 3} , and choice never change during a run of the program, will the if statements slow down my program?如果我要发送一个参数,比如说choice ,它只能是{1, 2, 3} ,并且choice在程序运行期间永远不会改变, if 语句会减慢我的程序吗? Example:例子:

arma::vec acceleration(arma::vec u, double t, int choice)
{   /*
    The current acceleration of the system.
    */

    if (choice == 1)
    {
        return acceleration_1(u);

    }

    else if (choice == 2)
    {
        return acceleration_2(u);

    }

    else if (choice == 3)
    {
        return acceleration_3(u);

    }
}

May a switch statement be better suited (faster) for this purpose? switch语句可能更适合(更快)用于此目的吗? Any alternative options which do not slow down the code?任何不会减慢代码速度的替代选项?

EDIT: acceleration and acceleration_x are member functions which make alternative solutions with function pointers hard.编辑: accelerationacceleration_x是成员函数,它们使函数指针的替代解决方案变得困难。

The short answer is, yes it will if you compare it to not having an if statement there at all.简短的回答是,是的,如果您将它与根本没有if语句进行比较,它会。 But that's not the answer you want.但这不是你想要的答案。

Your program is compiled and is run on a CPU.您的程序已编译并在 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.您没有提到您的项目针对哪种 CPU 架构,但大多数现代 CPU 使用分支预测,这有助于在条件可能在运行时发生变化时加快初始条件检查的速度。

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.即使在您的情况下,如您所说,选择变量在程序生命周期内不会改变,如果没有分支预测,编译器和 CPU 将没有太多机会避免每次在源代码中进行指示时检查它. 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.它是一个运行时变量,在 C++ 中没有结构可以帮助编译器或 CPU 知道它何时会发生变化。 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:他们可能会使用巧妙的启发式方法——比如用调用调度(函数调用)替换多个if语句,但如果您执行以下操作,则效果相同:

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 .但是, acceleration过程的唯一额外成本是调用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:回到分支预测,对于具有分支预测的 CPU,CPU 在做出相同选择一定次数后会“学习”:

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. ...意味着当你将acceleration_proc_id设置为某个选定的值时,一段时间后,CPU 将假设它会保持不变并预取并推测性地执行指令,就好像该选择是给定的一样。 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.与使用ifelseswitch几乎总是更好的选择,这当然适用于您的情况。 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.总而言之,CPU 可以运行的最佳代码根本就是没有代码——如果您需要在运行时切换或调用实际的、非内联的过程,则成本将在那里,而不是使用预处理器或常量表达式在编译程序时决定您想要哪种加速功能。

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.您最终需要对使用switch的程序与使用过程指针的程序进行概要分析,或者您可以想到的任何其他方法,我没有介绍过。 Profiling will determine where CPU spends most of its time anyway.无论如何,分析将确定 CPU 花费大部分时间的位置。 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.如果在分析之后您得出结论,与实际计算加速相比,能够在运行时决定一次加速过程的成本可以忽略不计,那么只需编写您可以编写的最易读、最短和最简单的代码,其余的留给编译器——这是它的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM