操作员链接,为什么长左分支?

时间:2015-01-24 01:00:43

标签: c++ performance compiler-optimization

根据this question(即OP表示相信并且未得到纠正) - 链式运营商向左增长:

a << b << c << d;
// ==
operator<<( operator<<( operator<<(a, b), c ), d);

为什么会这样?这样做会不会更有效率:

operator<<( operator<<(a, b), operator<<(c, d) );

即,要尽可能地平衡?

当然,编译器可以想出更好的运行时性能吗?

2 个答案:

答案 0 :(得分:3)

当您在C ++中重载运算符时,它保留了运算符未超载时的优先级和关联性。

对于移位运算符(<<>>),标准要求:

  

移位运算符&lt;&lt;和&gt;&gt;从左到右分组。

这意味着必须将a<<b<<c之类的操作解析为(a<<b)<<c

重载运算符会更改为每个操作调用的代码,但对分组没有影响 - 无论ab还是{{cint和{{{{}},分组都是相同的1}}是内置类型(例如,{{1}}),将使用编译器提供的运算符,或者它们是否为将使用重载运算符的某些类类型。处理分组的解析器无论如何都保持相同。

但请注意order of evaluation is independent of precedence or associativity,因此必然会影响执行速度。

答案 1 :(得分:1)

  1. 通过将车削操作转变为CISC,可以更好地实现“更好的运行时性能”。 (例如,MULADD。)
  2. 两个处理器之间的同步成本远远超过任何合理数量的运营商挤在一条线上的好处。
  3. 这会导致无序处理无法有效地使每个核心以Pentium Pro的速度运行。
  4. 除了Mystical提到的std::cout之外,请考虑以下整数算术问题:3 * 5 / 2 * 4应导致(15 / 2) * 4,意为28,但如果拆分,则会产生15 / 8,意味着1。即使运营商具有相同的重要性,情况也是如此。
  5. 修改

    很容易认为我们可以在核心之间拆分交换操作,例如3 * 5 * 2 * 4

    现在让我们考虑核心之间通信的唯一方法是通过共享内存。并且平均加载时间比平均CPU运行时间慢一个数量级。仅这一点就意味着在适应1次负载之前需要进行大量的数学运算才有意义。但更糟糕的是:

    1. 主核心必须将要执行的数据操作写入内存。因为这些必须是分开的,所以写入时可能会出现页面错误。
    2. 主核完成工作后,必须加载脏位以检查从核是否已完成处理。它可能很忙或已被抢占,因此可能需要多次加载。
    3. 然后必须加载从核心的结果。
    4. 你可以看到在负载费用的情况下会有多糟糕。让我们看看如果一切都保存在一个核心上就可以完成的优化:

      1. 如果提前知道某些值,则可以将它们分解为2,并且可以改为移位。更好的转变,并且可以一次添加可能比1更多的操作。
      2. 对于32位或更小的数字,64位处理器的高位和低位可用于添加。
      3. 乱序处理可以由CPU自行决定,例如浮点运算比整数运算慢,所以在这种情况下:1.2 * 3 * 4 * 5 CPU将先执行3 * 4 * 5并且只进行1次浮点操作。