C++ 效率比较:函数指针与函数对象与分支代码。为什么函数对象的性能最差?

C++ 效率比较:函数指针与函数对象与分支代码。为什么函数对象的性能最差?,c++,performance,function,lambda,overhead,C++,Performance,Function,Lambda,Overhead,我希望通过向嵌套循环中的函数调用传递函数指针或函数对象来提高性能,以避免循环分支。下面是三个代码:一个带有函数对象、函数指针和分支。对于任何编译器优化选项或任何问题大小,函数指针和对象版本都执行得最少。这让我感到惊讶;为什么函数指针或对象的开销会随着问题的大小而增加? 第二个问题。为什么函数对象的性能比函数指针差 更新 最后,我还添加了同一代码的lambda表达式版本。野蛮势力再次获胜。与相应的暴力代码相比,lambda表达式版本在有优化或无优化的情况下所需的时间是相应暴力代码的两倍多,并且针对

我希望通过向嵌套循环中的函数调用传递函数指针或函数对象来提高性能,以避免循环分支。下面是三个代码:一个带有函数对象、函数指针和分支。对于任何编译器优化选项或任何问题大小,函数指针和对象版本都执行得最少。这让我感到惊讶;为什么函数指针或对象的开销会随着问题的大小而增加? 第二个问题。为什么函数对象的性能比函数指针差

更新 最后,我还添加了同一代码的lambda表达式版本。野蛮势力再次获胜。与相应的暴力代码相比,lambda表达式版本在有优化或无优化的情况下所需的时间是相应暴力代码的两倍多,并且针对不同的问题大小

代码如下。使用
/a.out[SIZE][function choice]执行

功能对象:
#包括
#包括
类内插器
{
公众:
插值器(){};
虚拟双运算符()(左双,右双)=0;
};
类一阶:公共插值器
{
公众:
一阶(){};
虚拟双运算符()(左双,右双){返回2.0*左*左*左+3.0*右;}
};
类二阶:公共插值器
{
公众:
二阶(){};
虚拟双运算符()(左双,右双){返回2.0*左*左+3.0*右*右;}
};
双内核(双左,双右,插值器*int_func){return(*int_func)(左,右);}
int main(int argc,char*argv[])
{
双*a;
int SIZE=atoi(argv[1]);
int it=atoi(argv[2]);
//初始化
a=新的双[尺寸];
对于(int i=0;istd::cout虽然这个问题是关于性能的,但我有一些意见可以改进代码:

分支可能更容易出错 假设您要添加另一个插值函数。然后,您需要定义一个新函数并添加一个新的案例(用于开关)或一个新的if/else。解决方案可以是创建一个lambdas向量:

std::vector<std::function<double(double, double)>> Interpolate {
  [](double left, double right) {return 2.0*left*left*left + 3.0*right;}, //first order
  [](double left, double right) {return 2.0*left*left + 3.0*right*right;} //second order
};
而不是

if (it == 1)
    g += firstOrder(a[i], a[j]);
else if (it == 2)
    g += secondOrder(a[i], a[j]);
因此,所需的维护更少,错过if/else语句的可能性也更小

最好避免赤裸裸的新行为 与编写
double*a=newdouble[SIZE];
不同,人们建议使用
std::vector a(SIZE);
。这样我们就不需要释放任何资源,也避免了代码中潜在的内存泄漏


回到问题上来,我看不出lambdas有什么理由可以带来更好的性能。特别是在这种情况下,我们没有从中受益。

虽然这个问题是关于性能的,但我有一些意见可以改进代码:

分支可能更容易出错 假设您要添加另一个插值函数。然后,您需要定义一个新函数并添加一个新的案例(用于开关)或一个新的if/else。解决方案可以是创建一个lambdas向量:

std::vector<std::function<double(double, double)>> Interpolate {
  [](double left, double right) {return 2.0*left*left*left + 3.0*right;}, //first order
  [](double left, double right) {return 2.0*left*left + 3.0*right*right;} //second order
};
而不是

if (it == 1)
    g += firstOrder(a[i], a[j]);
else if (it == 2)
    g += secondOrder(a[i], a[j]);
因此,所需的维护更少,错过if/else语句的可能性也更小

最好避免赤裸裸的新行为 与编写
double*a=newdouble[SIZE];
不同,人们建议使用
std::vector a(SIZE);
。这样我们就不需要释放任何资源,也避免了代码中潜在的内存泄漏


回到问题上来,我看不出lambdas应该产生更好性能的原因。特别是,在这种情况下,与第四个选项相比,我们没有从中获益。

if
置于循环之外(尽管在编译器为您做这件事时没有区别).Function对象可能有更多的内存引用,因为您使用了虚拟函数,所以它必须根据对象类型查找正确的函数。@bipll我也尝试了使用-O0,结果是一样的。在这种情况下编译器也会这样做吗?@John3136我将在不使用虚拟函数的情况下执行另一个示例。@P.Nair优化,编译器并不是使分支情况更快的编译器。相反,这种情况很可能从编译器的优化中受益,并获得类似的好处。与第四个选项相比:将
if
置于循环之外(尽管这与编译器为您所做的没有区别).Function对象可能有更多的内存引用,因为您使用了虚拟函数,所以它必须根据对象类型查找正确的函数。@bipll我也尝试了使用-O0,结果是一样的。在这种情况下编译器也会这样做吗?@John3136我将在不使用虚拟函数的情况下执行另一个示例。@P.Nair优化时,编译器并不是使分支情况更快的编译器。相反,这种情况很可能受益于编译器的优化,并获得类似的好处。
std::vector<std::function<double(double, double)>> Interpolate {
  [](double left, double right) {return 2.0*left*left*left + 3.0*right;}, //first order
  [](double left, double right) {return 2.0*left*left + 3.0*right*right;} //second order
};
double firstOrder(double left, double right) {return 2.0*left*left*left + 3.0*right;}
double secondOrder(double left, double right) {return 2.0*left*left + 3.0*right*right;}
std::array<double(*)(double, double), 2> Interpolate {firstOrder, secondOrder};
g += Interpolate[it-1] (x, y);
if (it == 1)
    g += firstOrder(a[i], a[j]);
else if (it == 2)
    g += secondOrder(a[i], a[j]);