放宽执行顺序的规则

时间:2012-03-02 14:50:51

标签: c++ c optimization

C和C ++标准规定执行顺序必须严格遵循源指令顺序。编译器可以按任意顺序自由地评估指令中的子表达式,但不能重新排序用分号或冒号分隔的指令。

例如,在说明中:

A = A + B * C + D * E;

编译器可能选择在B * C之前执行D * E,或者在结尾添加A.

如果现在在各种指令中分割相同的计算:

int t1 = B * C;
t1 += D * E;
A += t1;

在这种情况下,编译器无法在B * C之后评估D * E.

通常,这种代码稍慢,因为编译器无法优化特定硬件的CPU指令顺序。

我想做的是相反的方式。例如,如果展开的循环的主体读取:

A[0] = B[0] * C[0];
A[1] = B[1] * C[1];
A[2] = B[2] * C[2];
A[3] = B[3] * C[3];

有没有办法告诉编译器这四条指令可以按任何顺序进行评估,因为它们对不同的数据进行操作?即使是非便携式技巧也是受欢迎的。

5 个答案:

答案 0 :(得分:9)

  

C和C ++标准规定执行顺序必须严格遵循源指令顺序。

不完全。他们说程序的可观察行为 - 即I / O操作和对易失性对象的访问 - 必须发生,好像就是这种情况。编译器仍然可以自由地重新排序评估,只要它不会改变程序的行为。只要您的赋值和乘法运算没有可观察到的副作用,您的代码就可以重新排序。

但是,当您使用指针或引用时,事情会更成问题。通常,编译器不能告诉ABC指向不同的内存区域,因此它必须假定A的赋值可能会改变一个稍后使用的BC值。因此,它无法重新排序评估。在C中,您可以使用restrict告诉编译器它们不重叠,但标准C ++中不存在此类功能。 (如果它们是数组,这不是问题,在这种情况下,编译器知道它们不重叠。)

答案 1 :(得分:2)

编译器只需要应用“as-if”规则。可观察行为必须保持预期,但如果编译器可以断定这些操作不会相互影响,则可以按任何顺序自由执行。

答案 2 :(得分:1)

您可以使用逗号运算符。 或者,如果目标平台是多核架构,则可以使用OpenMP。

A[0] = B[0] * C[0],
A[1] = B[1] * C[1],
A[2] = B[2] * C[2],
A[3] = B[3] * C[3];

P上。 S.实际上,使用逗号是不正确的答案。 因此,明确的并行性可能就是答案。或函数调用,如邻居答案。

答案 3 :(得分:1)

在C和C ++中,函数参数的评估顺序是未定义的。所以你可以这样做:

template<typename T> void f(T a, T b)
{
}

f( A[0] = B[0] * C[0], A[1] = B[1] * C[1] );

答案 4 :(得分:0)

这可能有效:

void eval_any_order(...) {}

eval_any_order(
  A[0] = B[0] * C[0],
  A[1] = B[1] * C[1],
  A[2] = B[2] * C[2],
  A[3] = B[3] * C[3]
);

编译器可能会对它们进行重新排序 - 只要结果相同,它就可以重新排序。