我正在求解一个耦合常微分方程系统,并且在我的程序运行之间我希望更改要集成的函数。到目前为止,我只是简单地注释和取消注释三个返回语句以在不同的函数之间切换,如下所示:
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
被多次调用,我想让代码高效,但我也想更改哪个函数与输入参数集成,而不是通过输入代码的深度来注释和取消注释不同的语句。如果我要发送一个参数,比如说
choice
, 只能是 {1, 2, 3}
之一, 和 choice
在程序运行期间永远不会改变,if 语句会减慢我的程序吗?例子: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);
}
}
5 月
switch
声明是否更适合(更快)用于此目的?任何不会减慢代码速度的替代选项?编辑:
acceleration
和 acceleration_x
是成员函数,它们使具有函数指针的替代解决方案变得困难。
最佳答案
简短的回答是,是的,如果你把它比作没有 if
在那里声明。但这不是你想要的答案。
您的程序已编译并在 CPU 上运行。您没有提及您的项目针对哪种 CPU 架构,但大多数现代 CPU 使用分支预测,当条件在运行时可能发生变化时,这有助于加速幼稚条件检查的情况。
即使在您的情况下,正如您所说,选择变量在程序生命周期内不会改变,如果没有分支预测,编译器和 CPU 也没有太多机会避免每次在源代码中指示时检查它.它是一个运行时变量,对于 C++,没有任何结构可以帮助编译器或 CPU 知道它何时会改变。他们可能会使用巧妙的启发式方法——比如替换 if
的数字。带有调用调度(函数调用)的语句,但如果您执行以下操作,那将是相同的:
arma::vec (*chosen_acceleration_proc)(arma::vec, double);
arma::vec acceleration(arma::vec u, double t)
{
return chosen_acceleration_proc(u, t);
}
您可以在运行时切换所选过程:
chosen_acceleration_proc = acceleration_1; /// For example
以上将阻止编译器 inlining你不同的加速程序,否则它可以。但是,您的
acceleration
的唯一额外费用程序正在调用 chosen_acceleration_proc
.完成所选加速过程的成本可能远远超过调用它的成本。这里的底线是,如果加速过程可以从内联中受益,那么使用过程指针是一种不合适的方法。您必须分析您编译的程序,以查看此类调度的成本是否可以忽略不计或显着。
回到分支预测,对于具有分支预测的 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
}
}
...意味着当您设置
acceleration_proc_id
到某个选定的值,一段时间后,CPU 将假设它将保持不变,并预取并推测性地执行指令,就好像该选择是给定的一样。见 this most excellent answer有关分支预测及其如何帮助或失败的更多信息。一个
switch
几乎总是比使用 if
更好的选择和 else
,当然是你的情况。这部分是因为人们可能只使用常量表达式来打开,这对于优化编译器很有用。有other reasons .如果不同的过程都是一个类的所有成员怎么办?
如果不同的加速计算过程属于同一类或其派生类,您仍然可以使用指向成员过程的指针。如果它们都是具有相同签名的不同成员,则在同一类中:
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);
}
不管你如何设计你的应用程序——无论它是一个包含不同加速过程的“应用程序”对象还是应用了一些其他语义,你都可以像上面那样将指针指向所需的过程,这实际上并没有什么不同用“普通”的程序。
总而言之,CPU 可以运行的最佳代码是根本没有代码——如果您需要在运行时切换或调用实际的、非内联的过程,则成本将在那里,而不是使用预处理器或常量表达式在编译程序时决定您想要的加速功能。
您最终需要对使用
switch
的程序进行概要分析。与使用过程指针的方法相比,或者您可以想到的任何其他我没有介绍的方法。无论如何,分析将确定 CPU 大部分时间花在哪里。如果在分析之后您得出结论,与实际计算加速相比,能够在运行时决定一次加速过程的成本可以忽略不计,那么只需编写您可以编写的最易读、最短和最简单的代码,其余的留给编译器——这就是它的工作。
关于c++ - 条件不变的 if 语句会减慢我的 C++ 代码吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59235896/