算子链,为什么要长左枝

Operator chaining, why grow left branch?

本文关键字:为什么      更新时间:2023-10-16

根据这个问题(即OP陈述了信念并且没有被纠正) - 链式运算符向左增长:

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

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

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

即,尽可能平衡?

编译器当然可以解决这个问题以获得更好的运行时性能吗?

在 C++ 中重载运算符时,它保留与运算符未重载时相同的优先级和关联性。

对于轮班操作员(<<>>),标准要求:

轮班操作员从左到右<<和>>分组。

这意味着像 a<<b<<c 这样的操作必须解析为 (a<<b)<<c

重载运算符会更改为每个操作调用的代码,但对分组没有影响 - 无论abc是将使用编译器提供的运算符的内置类型(例如,int),或者它们是否是将使用重载运算符的某种类类型,分组都将相同。无论哪种方式,处理分组的解析器都保持不变。

但请注意,计算顺序与优先级或关联性无关,因此这不一定影响执行速度。

    通过将
  1. 转换操作转换为 CISC,可以更好地实现"更好的运行时性能"。(例如 MULADDs。
  2. 两个处理器之间的同步成本将远远超过挤在一条生产线上的任何合理数量的操作员的好处。
  3. 这将使无序处理无法有效地使每个内核以奔腾 Pro 的速度运行。
  4. 除了 Mystical 提到的std::cout之外,想想下面的整数算术问题:3 * 5 / 2 * 4应该导致 (15 / 2) * 4 ,意思是28,但如果拆分出来,它会导致15 / 8,意思是1。即使运算符具有相同的意义,也是如此。

编辑:

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

现在让我们考虑一下,在内核之间进行通信的唯一方法是通过共享内存。并且平均加载时间比平均 CPU 运行时间慢一个数量级。仅此一项就意味着在容纳 1 个负载之前需要完成大量的数学运算。但更糟糕的是:

  1. 主核心必须将要执行的数据操作写入内存。由于它们必须是分开的,因此写入时可能会出现页面错误。
  2. 主内核完成后,它必须加载一个脏位来检查从内核是否已完成处理。它可能正忙或已被抢占,因此这可能需要多个负载。
  3. 然后必须加载来自从机核心的结果。

你可以看到考虑到负载的费用,这会有多糟糕。让我们看一下如果所有内容都保留在一个内核上可以完成的优化:

  1. 如果提前知道某些值,则可以将它们分解为2s,并且可以进行偏移。更好的班次和添加可能一次执行超过 1 个操作。
  2. 对于 32 位或更小的数字,可以使用 64 位处理器的上位和下位进行加法。
  3. 无序处理可以由 CPU 自行决定,例如浮点运算比整数运算慢,因此在以下情况下: 1.2 * 3 * 4 * 5 CPU 将首先执行3 * 4 * 5,并且只执行 1 个浮点操作。