如何解决如果通过限制指针发生,实现可以优化对非原子访问的原子访问吗?
考虑以下函数。
void incr(_Atomic int *restrict ptr) {
*ptr += 1;
}
我会考虑 x86,但我的问题是关于语言,而不是任何特定原子实现的语义。 GCC 和 Clang 都发出以下信息:
incr:
lock add DWORD PTR [rdi],1
ret
符合要求的实现是否可以简单地发出
incr:
add DWORD PTR [rdi],1
ret
(删除 _Atomic
后得到的结果相同。)
如果没有 restrict
,这将是一个错误编译,因为 add
不是原子的,因此(例如)在两个线程中同时调用 incr
会发生竞争。但是,由于指针是 restrict
限定的,我认为在 incr
和对 *ptr
的任何其他访问之间不可能发生竞争(无论如何都不会导致未定义的行为)。>
我有信心手动进行此优化,但我所知道的任何编译器都不会自动执行此操作。这是对的吗?还是我误解了restrict
?
解决方法
编译器无法进行优化,因为 restrict
的行为仅针对通过 incr
内的其他指针访问同一对象而定义。
来自this reference,我自己强调:
在声明了受限指针 P 的块的每次执行期间(通常是每次执行 P 是函数参数的函数体),如果某个可通过 P(直接或间接)访问的对象被修改,无论如何,对该对象的所有访问(读取和写入) 都必须通过 P(直接或间接)发生,否则行为未定义。 .
对指针的限制仅适用于从 incr
进行的访问,这意味着符合标准的编译器不应该能够得出任何关于其他线程或其他具有自己的指针指向的函数的访问的结论相同的整数(这完全没问题,incr
的调用者可能只有指向它的不受限制的指针)。
开放标准第 6.7.3.1 节还定义了 restrict
与正在执行的块相关的形式行为,不对任何指针的生命周期做出任何声明。但是,我认为该标准的定义在数学上过于笨拙,不值得在此完整呈现。
还是我误解了限制?
是的 - 它不能保证在您使用它的函数之外会发生什么。因此它不能用于同步或线程安全目的。
关于 restrict
的 TL;DR 是 *restrict ptr
保证不会从 ptr
所在的块对 ptr
指向的对象进行其他指针访问宣布。如果存在其他指针或对象引用(“左值访问”),编译器可能会假定它们没有修改指向的受限对象。
这几乎只是程序员和编译器之间的契约:“亲爱的编译器,我保证我不会用这个指针指向的对象在你背后做愚蠢的事情”。但是编译器无法真正检查程序员是否违反了这个约定,因为为了做到这一点,它可能必须同时检查多个不同的翻译单元。
示例代码:
#include <stdio.h>
int a=0;
int b=1;
void copy_stuff (int* restrict pa,int* restrict pb)
{
*pa = *pb; // assign the value 1 to a
a=2; // undefined behavior,the programmer broke the restrict contract
printf("%d\n",*pa);
}
int main()
{
copy_stuff(&a,&b);
printf("%d\n",a);
}
这在所有主流 x86 编译器上打印 1
然后 2
并优化。因为在函数内部的 printf 中,他们可以自由地假设 *pa
自 *pa = *pb;
以来没有被修改。删除 restrict
,它们将打印 2
然后是 2
,因为编译器必须在机器代码中添加额外的读取指令。
因此,要使 restrict
有意义,需要将它与另一个对对象的引用进行比较。在您的示例中情况并非如此,因此 restrict
没有明显的用途。
至于_Atomic
,它总是必须保证机器指令本身是原子的,不管程序中发生了什么。这不是一些半高级别的“反正似乎没有其他人在使用这个变量”。但是要使关键字对多线程和多核都有意义,它还必须充当低级内存屏障,以防止流水线、指令重新排序和数据缓存——如果我没记错的话,这正是 x86 lock
确实如此。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。