使用简写IF的C++编译器优化

C++ compiler optimization with shorthand IF

本文关键字:C++ 编译器 优化 IF      更新时间:2023-10-16

当涉及到人手不足的if/else时,我有一个关于编译器优化的问题。

我有这个功能:

double eu_distance (const coor& x, const coor& y) {
return ((y.x - x.x)*(y.x - x.x) + (y.y - x.y)*(y.y - x.y));
}

我想知道什么更有效?

min = min > eucl_distance(point_a, point_b) ? eucl_distance(point_a, point_b) : min;

double dis = eucl_distance(point_a, point_b);
if (min > dis)
    min = dis;

在前一种情况下,编译器(在我的情况下,GCC 4.6.2)知道如何优化if/else,以保持eucl_dancee()的返回值可重用,而不是计算两次吗?

一个背负式的问题是:

什么更有效?

(y.x - x.x)*(y.x - x.x)

pow((y.x - x.x),2)

PS:很抱歉,我不能选择多个正确答案!!:(感谢大家的回答!我真的很感激!!

没有通用的答案:您必须对生成的代码进行分析通过您的实施来确定。然而,在大多数情况下,如果eu_distance在一个单独的翻译单元中,并不特别注释,编译器将无法知道调用它两次具有相同的论点将给出相同的结果;在这种情况下第二种形式肯定会更快。另一方面,如果eu_distance可以内联,任何合适的优化器最终都会为两者生成几乎完全相同的代码。

在实践中,我几乎肯定会使用第三种形式:

min = std::min( eu_distance( point_a, point_b ), min );

(我假设eucl_distanceeu_distance的拼写错误。)

此外,我会避免使用min这样的名称。有人太可能添加using namespace std;之后,甚至包括<windows.h>,不包括hvaving定义的CCD_ 8。(<windows.h>minmax定义为宏(如果尚未定义NOMINMAX)。这导致了一些如果您定义了自己的minmax,则会出现有趣的错误消息。或甚至包括CCD_ 15。)

关于pow( x, 2 ):再说一遍,你真的必须测量,但是通常,x * x会更快,即使x是一个复杂的表示(当然,如果表达式不是平凡的,那么认识到两个x是相同的可能并不那么容易使代码更难阅读。在这种情况下,您可能需要考虑一个小函数,比如squared,它只返回x * x。如果它在性能上有所不同,则将其内联。)

所有关于优化器如何处理代码的问题都很难解决,因为有很多变量会影响优化器。答案的第一部分是,在任何情况下,缓存函数的结果并重用它都不会比其他方法更糟糕,所以我会继续使用第二种方法。

至于第一种方法可以优化什么,这取决于代码的布局方式。如果编译器可以访问euclid_distance的定义,并且可以内联它,那么它可以有效地确定函数是,并将在第二次调用中产生相同的输出,因此它可能会缓存第一次调用的值。如果编译器无法访问函数的定义,那么它无法知道对函数的第一次和第二次调用是否会产生相同的结果(考虑rand(),每次调用都会产生不同的数字),因此它将调用函数两次。您可以通过函数添加编译器提示来提供帮助。您可以在gcc中搜索__pure属性,如果我记得正确的话,它将在这种情况下帮助优化器。

关于pow与纯乘法的使用,再次,对于2的幂,我只使用直接乘法。CCD_ 26基本上不可能使其更有效。在这种情况下,您不需要缓存y.x-x.x,因为编译器看到它被重用,并且可以为您这样做。

double dis = eucl_distance(point_a, point_b);
if (min > dis)
    min = dis;

比这个更有效率:

min = min > eucl_distance(point_a, point_b) ? eucl_distance(point_a, point_b) : min;

只是因为您取消了一个额外的呼叫eucl_distance(point_a, point_b)

但是,如果您将函数标记为"纯",或者编译器可以看到定义并确定它是纯的,它将消除对它的额外调用,就像消除重复的数学表达式一样,然后生成的代码将几乎相同。然而,你不能总是去检查组装等,所以我会坚持第一种情况,它更干净。

至于您的背负式问题,(y.x - x.x)*(y.x - x.x)更快(您可以更具体地将y - x的结果存储在tmp变量中)。。。然而,编译器很清楚pow函数,因为第二个参数是编译时表达式,所以它可以很容易地展开代码以匹配手动编码的乘法。但是,你不能真的依赖你,所以如果你能让代码更干净、更明显,那就去做吧。例如,我看不出你有任何理由想要调用pow(something, 2)

永远记住,过早的优化是没有好处的。但是,首先也不要写糟糕的慢代码:)

我会说:

  • 不要假设任何编译器行为
  • 重用结果以避免不必要的调用

没有什么告诉编译器您的eucl-dance函数是完全等幂的。因此,它不可能跳过假设缓存结果的额外调用。(如果你在通话中增加一个静态计数器变量怎么办??)

对于乘法与pow的问题,我猜测这取决于底层处理器的数学优化。