【发布时间】:2018-05-16 07:48:45
【问题描述】:
我刚刚测试了一个小例子来检查__restrict__ 是否在最新编译器上的 C++ 中工作:
void foo(int x,int* __restrict__ ptr1, int& v2) {
for(int i=0;i<x;i++) {
if(*ptr1==v2) {
++ptr1;
} else {
*ptr1=*ptr1+1;
}
}
}
使用最新的 gcc (gcc8.1 -O3 -std=c++14) 在 godbolt.org 上尝试时,__restrict__ 按预期工作:v2 仅加载一次,因为它不能与ptr1.
以下是相关的组装零件:
.L5:
mov eax, DWORD PTR [rsi]
cmp eax, ecx # <-- ecx contains v2, no load from memory
jne .L3
add edx, 1
add rsi, 4
cmp edi, edx
jne .L5
现在与最新的 clang (clang 6.0.0 -O3 -std=c++14) 相同。它展开循环一次,因此生成的代码要大得多,但要点如下:
.LBB0_3: # =>This Inner Loop Header: Depth=1
mov edi, dword ptr [rsi]
cmp edi, dword ptr [rdx] # <-- restrict didn't work, v2 loaded from memory in hot loop
jne .LBB0_9
add rsi, 4
mov edi, dword ptr [rsi]
cmp edi, dword ptr [rdx] # <-- restrict didn't work, v2 loaded from memory in hot loop
je .LBB0_12
为什么会这样?我知道__restrict__ 是非标准的,编译器可以随意忽略它,但它似乎是从代码中获得最后一点性能的非常基本的技术,所以我怀疑clang 根本不支持它同时支持和忽略关键字本身。那么,这里的问题是什么?我做错什么了吗?
【问题讨论】:
-
可能是因为
__restrict__从未在 C++ 标准中定义,只是一个 gcc 扩展? -
它不仅是一个 GCC 扩展,它还是一个可以安全忽略的扩展。它只影响效率,不影响正确性。
-
@Bob__:我做到了。不是这个例子,而是来自我们真实代码的例子。这就是为什么我首先在玩
__restrict__。我没有因为过早的优化而得到报酬;)。我们的热循环非常紧凑,额外的内存负载使我们的性能下降。 -
@MSalters:我知道。当然可以忽略。但它仍然非常可行。仅仅因为某些 can 被忽略并不意味着现代编译器会这样做。在优化潜力方面,clang 通常在大多数编译器之上。我只是觉得奇怪,他们会不会忽略这种情况下的潜力。
-
@MSalters:当然可以。在这个最小的例子中。但是,如果您在一个传递给热循环的 lambda 中并且捕获了
v2怎么办?然后你不能在循环之前把它放到堆栈上,因为你(即 lambda)不控制循环。