考虑以下代码:
double computeK(unsigned short choice, double R){
switch (choice){
case 1:
return 1.0/R;
break;
case 2:
return log(R);
break;
case 3:
return exp(-R);
break;
case 4:
return exp(-R*R);
break;
case 5:
return exp(-R);
break;
default:
return 1.0/R/R;
break;
}
}
用户对来自其输入文件中调用的另一个类的R
的不同值多次调用此函数,但choice
的值对于每个输入文件保持不变。有没有办法避免这个switch语句? (我可以编写一个makefile,为choice
添加适当的标记,并要求用户更改makefile中tag的值,但是用户想要从输入文件调用该函数而不从makefile进行更改。)< / p>
如果您能提供相关代码,我将不胜感激。
由于
答案 0 :(得分:3)
C ++方法如下:
更新因为它显然不是一成不变的,所以这是固定的:
choice
对于所有值都保持不变)。该开关 仅执行一次 computeK
调用 (这不一定是函数的情况指针虽然编程器现在很聪明,但偶尔会发现这样做的能力)。当呼叫被实际计算包围时,这可能会成为一个巨大的性能胜利。代码:
#include <vector>
#include <cmath>
template <typename ComputeK>
void RunBatch(std::vector<double> const& data, ComputeK const& computeK)
{
for (double element : data)
{
double k = computeK(element); // TODO useful work
}
}
int main()
{
int choice = 3; // TODO input
std::vector<double> data(10000); // Demo only, could be streamed from input
switch (choice){
case 0: RunBatch(data, [] (double R) { return 1.0/R; }); break;
case 2: RunBatch(data, [] (double R) { return log(R); }); break;
case 3: RunBatch(data, [] (double R) { return exp(-R); }); break;
case 4: RunBatch(data, [] (double R) { return exp(-R*R); }); break;
case 5: RunBatch(data, [] (double R) { return exp(-R); }); break;
default: RunBatch(data, [] (double R) { return 1.0/R/R; }); break;
}
}
答案 1 :(得分:2)
如果在编译时知道choice
,您可以使用模板选择要调用的专业化:
template<int choice>
void computeK(double R);
template<>
void computeK<1>(double R)
{
return 1.0/R;
}
template<>
void computeK<2>(double R)
{
return log(R);
}
你打电话给:
computeK<2>(R);
答案 2 :(得分:2)
使用&#34;未知&#34;开启choice
值比计算1.0/R
快4到10倍,比exp(R);
或log(R);
快10到100倍
如果choice
始终相同,则分支预测将启动并决定&#34;哦,它通常在这里2,所以让我们预加载并开始执行选择2&#34;。
使用函数指针可能不值得付出努力,因为你最终会调用而不是可预测的条件跳转 - 所以不太可能给你任何好处。
如果choice
是调用代码中的硬编码常量,则将此函数放在头文件中,让编译器内联它将删除整个开关。但如果它是从文件读取的变量,那么它就是一个不同的故事。
最后,也许对代码进行不同的拆分会有所帮助 - 例如而不是传递单个double
,传递const vector<double>& R, vector<double> K
并从K
值计算{,1}}值的10,100或100000000值。
与处理优化/性能,基准测试时一样,比较不同的解决方案,最好是在多个平台上。
答案 3 :(得分:1)
首先,关于剖析和东西的常见做法。
其次,switch
将尽可能高效。它是简单的连续值,编译器可以将其简单地转换为函数局部跳转表。另外,如果你继续使用相同的choice
进行调用,那么分支预测将使CPU在零时间内(如果不是有时绝对地)执行这个简单的模式。
请注意,所有基于存储函数指针或函数对象的解决方案都是跳转表,它们只是非本地的,编译器优化起来要困难得多 - &gt;性能较慢。
编辑:事实上,如果你可以说服用户在Makefile中提供值,那么模板将是一个非常可行的选择。我把这个问题看作是禁止这个问题,但其他的回答者并没有这样做,所以它有点含糊不清。
答案 4 :(得分:0)
简单地将您的大功能分解为几个更简单的功能,例如:
double computeK3(double R){
return exp(-R);
}
当您正在读取输入时,确定要调用哪个函数(可能通过switch语句),然后将指针存储到该函数。
现在,您可以使用该指针调用正确的函数。
在这种情况下,你只会经历一次switch语句,所以这是一个改进。
答案 5 :(得分:0)
使用带有函数对象或函数指针的表驱动 例如:
typedef double (*FunPtr)(double r);
FunPtr Handle_Table[6] = {handle1Fun, handlele2Fun....};
....
return Handle_Table[choise](r);
进行。
答案 6 :(得分:-1)
我会创建一个函数指针数组,每个函数代表一个不同的操作:
std::vector<double (*func)(double)> opList;
定义每个索引(例如选项)的某处以指向不同的函数,定义函数以便它们接收R值并返回结果。然后,代替switch语句,访问opList数组的数组索引并像函数一样使用它:
double computeK(unsigned short choice, double R) {
return opList[choice-1](R);
}
函数指针列表中的每个函数都如下所示:
double Choice1(double R) {return return 1.0/R};