我有几个相似的函数,比如A,B,C,我想用命令行选项选择其中一个,而且,我调用了这个函数数十亿次,因为我定义了一个函数指针Phi,并只设置一次,到所需的函数,而不是检查函数中的变量数十亿次,但是当我设置,Phi = A,(所以不考虑用户输入)我的代码运行约24秒,当我添加一个if-else并设置Phi为所需的函数,我的代码运行约30秒与完全相同的参数.(当然命令行选项设置Phi为A)什么是有效的方式来处理这种情况?
我的职责:
double funcA(double r)
{
return 0;
}
double funcB(double r)
{
return 1;
}
double funcC(double r)
{
return r;
}
void computationFunctionFast(Context *userInputs) {
double (*Phi)(double) = funcA;
/* computation codes */
}
void computationFunctionSlow(Context *userInputs) {
double (*Phi)(double);
switch (userInputs->funcEnum) {
case A:
Phi = funcA;
break;
case B:
Phi = funcB;
break;
case C:
Phi = funcC;
}
/* computation codes */
}
我试过gcc,clang,icx的-O2和-O3优化。(gcc在上述情况下没有性能差异,但性能最差)虽然我使用的是C,我也试过std::function。我试过在不同的作用域中定义Phi函数等。
3条答案
按热度按时间qnyhuwrf1#
一般来说,这里有几件事对性能稍有不利:
下面是一个基于您的代码的示例:
叮当声15.0.0 x86_64-O3给出:
即使我选择的数字是相邻的,通常的编译器也无法优化出比较
cmp
,即使我包含了一个default: return 0;
,它仍然存在,你可以很容易地手动优化任何具有连续索引的switch
到函数指针跳转表中,如下所示:叮当声15.0.0 x86_64-O3给出:
这使得代码稍微好一点,因为比较指令/分支现在被删除了。然而,这实际上是一个微观优化,不应该对性能有太大影响。你必须确定它是否有任何改进。
(Also gcc 12.2没有很好地优化这段代码,所以我在这个例子中使用了clang。)
Godbolt链接:https://godbolt.org/z/ja4zerj7o
dgjrabp22#
没有更“有效”的方法来处理这种情况,你已经在做你应该做的了。
您观察到的时间差异是因为:
1.在第一种情况下(
Phi = funcA
),编译器知道函数总是相同的,因此能够优化它的调用。根据你的“计算代码”,这可能意味着内联函数并为你简化大量的计算。1.在第二种情况下(
Phi = <choice from user>
),编译器不知道哪个函数会被选中,因此无法优化代码的其他部分对它的调用,也无法像第一种情况那样将优化传播到“计算代码”的其他部分。一般来说,你能做的并不多,动态函数指针本身就增加了一些运行时开销,使优化变得更困难(或者不可能)。
你可以尝试在不同的函数或不同的分支中复制“计算代码”,这些代码是在Assert
Phi
等于一个常数之后才输入的,如下所示:在上面的代码中,编译器知道在任何
if
块中,Phi
的值只能有一个值,因此 * 可以 * 执行上面第1点中讨论的相同优化。c3frrgcw3#
当你使用
enum
来选择一个函数指针时,你不需要在你的userInputs
中放置一个enum
,直接在结构中添加函数指针就可以了,并且消除了每次调用时的分支。代替
使用
你会得到这样的结果: