避免循环中的条件语句和函数调用
Avoiding conditionals and function invocations inside a loop
我有一个代码如下:
void function(int parameter)
{
for( ... ) // a big loop
{
double a = ...;
for( ... ) // a big loop
{
double b = ...;
double value;
if(parameter == 1)
value = some_math_expression_1(a, b);
else if(parameter == 2)
value = some_math_expression_2(a, b);
...
}
}
}
这个想法是,根据参数,我想对a
和b
应用一些数学表达式。这个函数执行了很多次,而且必须很快,我想知道每次迭代时的条件分支是否会引入我可以节省的开销。
现在,我已经写了这样的代码:
void function(int parameter)
{
if(parameter == 1)
function1();
else if(parameter == 2)
function2();
else
...
}
这样,如果我在每个functionX()
中重复代码,我就可以直接应用数学表达式。显而易见的问题是,当我想更改某段代码时,我必须做几次(我现在有大约10个数学表达式)。
我可以使用什么方法来避免function
中的任何开销?
如果我传递一个指向函数some_math_expression_X
到function
的指针(我会更改函数调用的条件),该怎么办?
如果我将整个函数编码为宏(uf),并将数学表达式设置为参数,会怎么样?
如果我使用模板并将数学表达式作为指向内联函数的指针传递(这可能吗),该怎么办?
编辑:感谢您的回答。我知道我可以使用你提出的方法(指向函数数组的指针,或者依赖于分支预测器)。然而,你对避免开销方面什么会更好有一些见解吗?数学表达式非常简单(类似于a*b
),除了长循环之外,function
还被调用多次(分支预测在调用之间"存活"吗?)。
您可以将函数转换为模板:
void functionT<int PARAMETER>()
{
for( ... ) // a big loop
{
double a = ...;
for( ... ) // a big loop
{
double b = ...;
double value;
if(PARAMETER == 1) //Constant condition!!!
value = some_math_expression_1(a, b);
else if(PARAMETER == 2) //Constant condition!!!
value = some_math_expression_2(a, b);
...
}
}
}
由于条件总是true或false,编译器将优化条件树,只留下真正的数学表达式。没有分支,也没有函数调用!
现在,你只能在参数不变的情况下使用它:
functionT<1>();
但没有变量:
int x = 1;
functionT<x>(); //Error
如果你需要,你可以做一个包装:
void function(int parameter)
{
switch (parameter)
{
case 1: functionT<1>(); break;
case 2: functionT<2>(); break;
}
}
别担心。现代CPU有分支预测器,它们可以正确地预测所采取的分支。
您可以设置一个函数指针的常量数组,并调用与parameter
关联的函数。
但是,如果数学表达式很小,switch()语句可能会更快。
switch (parameter) {
case 1:
value = math expression 1;
break;
case 2:
...
}
首先,我会一如既往地说,您现在应该基准测试/衡量这个过程需要多长时间,因为一如既往,这可能是过早的优化,您可能会发现这不是代码中需要很长时间的部分。
但假设你已经测量并发现这是你代码中的瓶颈,我会做一些事情
首先,正如你所说,在这里(只要你的数学函数足够简单)最会让你丧命的是分支预测。因此,为了摆脱分支,我会创建一个函数指针数组,而不是进行
if(parameter == 1)
function1();
if...
你可以做:
array_of_functions[parameter]();
这将消除所有分支预处理,并将大大提高吞吐量,因为您的管道将不必冲洗。编译器也应该能够内联函数。
它取决于许多因素,但通常情况下,您可能希望使它在大多数情况下连续调用第一个或第二个函数。这将使现代CPU执行速度更快(请参阅为什么处理排序数组比处理未排序数组更快?)。
您可以使用数组和函数指针,但这可能不会加快速度,需要尝试。您可以使用http://www.boost.org/doc/libs/1_54_0/doc/html/function/tutorial.html#idp59212272以提供帮助,但静态函数不需要它。
我认为最有效的方法之一是创建一个函数指针数组,然后可以直接传递函数指针,而不仅仅是参数。这将节省在嵌套循环中使用if/switch语句所产生的任何类型的开销。
例如:
double expression_0(double a, double b) {...};
double expression_1(double a, double b) {...};
void function(double (*expression)(double, double)) {
for (...) {
...
double a = ...;
for (...) {
double b = ...;
double result = (*expression)(a, b);
}
}
}
int main() {
double (*fpointers[2]) (double, double);
fpointers[0] = expression_0;
fpointers[1] = expression_1;
int parameter = ...;
function(fpointers[parameter]);
}
如果所有函数都有相同的签名,那么最简单的方法就是:
void function(int parameter)
{
double ( *fn )( double, double );
switch( parameter )
{
case 1: fn = &some_math_expression_1; break;
case 2: fn = &some_math_expression_2; break;
...
}
for( ... ) // a big loop
{
double a = ...;
for( ... ) // a big loop
{
double b = ...;
double value = fn( a, b );
...
}
}
}
- 函数调用中参数的顺序重要吗
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 在可变函数调用中执行一次语句
- 在 if 语句中调用重载构造函数失败
- C++实用程序,用于将长开关语句转换为封装开关案例阶梯的简洁函数调用
- 如果语句表达式调用函数,则需要测试是否为 true
- 在返回语句中调用默认构造函数
- 递归可变参数函数调用对简单 if.else 语句的性能
- 可以在 main 的返回 0 语句之后调用任何函数吗?
- 在 CPP 中将函数调用定时为 if 语句条件
- 开关语句中的函数调用
- C++:在最后一个语句之前调用析构函数的时间
- 在同一语句中调用的执行 IO 的函数:未定义或未指定
- 如何在 cout 语句中调用 void 函数
- 避免循环中的条件语句和函数调用
- 在语句中调用 std::set 函数时出现意外"if"评估结果
- 哪一个更快?函数调用或条件if语句
- gcc-gprof/gcov/other-如何获取函数调用/出口+控制流语句的序列
- 如果函数调用是返回语句,编译器可以自动移动函数参数吗?
- 如何使用在C++中具有多个参数的if语句来调用多个函数