使用简写IF的C++编译器优化
C++ compiler optimization with shorthand IF
当涉及到人手不足的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_distance
是eu_distance
的拼写错误。)
此外,我会避免使用min
这样的名称。有人太可能添加using namespace std;
之后,甚至包括<windows.h>
,不包括hvaving定义的CCD_ 8。(<windows.h>
将min
和max
定义为宏(如果尚未定义NOMINMAX
)。这导致了一些如果您定义了自己的min
或max
,则会出现有趣的错误消息。或甚至包括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的问题,我猜测这取决于底层处理器的数学优化。
- C/C++编译器通常会删除重复的库吗
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- Win32编译器选项和内存分配
- MSVC多行宏编译器错误
- 静态数据成员的问题-修复链接错误会导致编译器错误
- C++,我收到一个无法理解的编译器错误
- 在线编译器中的分段C++没有打印消息
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- C/C++预处理器是否可以检测一些编译器选项
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 我需要知道编译器如何在cpp中使用析构函数
- 编译器如何区分std::vector的构造函数
- CLANG 编译器 说:变量"PTR"可能未初始化
- 告诉c++编译器该参数没有别名
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么所有C++编译器都会崩溃或挂起此代码
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 我收到同义重复编译器错误。我应该如何修复"类型"X"的参数与类型"X"的参数不兼容?
- 为什么C++编译器没有检测到正确声明的类?