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];
有没有办法告诉编译器这四条指令可以按任何顺序进行评估,因为它们对不同的数据进行操作?即使是非便携式技巧也是受欢迎的。
答案 0 :(得分:9)
C和C ++标准规定执行顺序必须严格遵循源指令顺序。
不完全。他们说程序的可观察行为 - 即I / O操作和对易失性对象的访问 - 必须发生,好像就是这种情况。编译器仍然可以自由地重新排序评估,只要它不会改变程序的行为。只要您的赋值和乘法运算没有可观察到的副作用,您的代码就可以重新排序。
但是,当您使用指针或引用时,事情会更成问题。通常,编译器不能告诉A
,B
和C
指向不同的内存区域,因此它必须假定A
的赋值可能会改变一个稍后使用的B
或C
值。因此,它无法重新排序评估。在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]
);
编译器可能会对它们进行重新排序 - 只要结果相同,它就可以重新排序。