再次波动:防止优化所必需
Once more volatile: necessary to prevent optimization?
我已经阅读了很多有关'挥发性'关键字的信息,但我仍然没有确定的答案。
考虑此代码:
class A
{
public:
void work()
{
working = true;
while(working)
{
processSomeJob();
}
}
void stopWorking() // Can be called from another thread
{
working = false;
}
private:
bool working;
}
当工作()进入其循环时,"工作"的价值是正确的。
现在,我猜允许编译器在(工作) to 时优化,而(true)是'working'的值开始循环时。
- 如果不是这种情况,那将意味着这样的事情效率很低:
for(int i = 0; i < someOtherClassMember; i++) { doSomething(); }
...由于必须加载某些hotherclassmember的值。
- 如果此为,我认为"工作"必须是,以防止编译器优化。
这两个情况中的哪一个?当搜索使用 domatile 的使用时,我发现人们声称它只有在使用I/O设备直接写入内存时才有用,但是我也发现应该在像我这样的场景中使用它。
您的程序 Will 被优化到无限环†中。
void foo() { A{}.work(); }
被编译为(使用O2)
foo():
sub rsp, 8
.L2:
call processSomeJob()
jmp .L2
该标准定义了假设的抽象机器对程序有什么作用。符合标准的编译器必须编译您的程序,以与所有可观察的行为中的机器相同。这被称为 as-if 规则,编译器具有自由度,只要您的程序做什么,无论如何如何。
通常,读取和写入变量并不构成可观察到的,这就是为什么编译器可以尽可能多地读取和写作的原因。编译器可以看到working
不会分配给并优化读书。volatile
的(通常被误解)效果完全是为了使它们可观察到,这迫使编译器留下读取并单独写入‡。
但是,等等,另一个线程可能分配给working
。这是不确定行为的余地所在的地方。编译器可能会在不确定的行为时做任何事情,包括格式化硬盘驱动器并仍然是标准符合的。由于没有同步,并且working
不是原子化,因此任何其他写入working
的线程都是数据竞赛,这是无条件未定义的行为。因此,无限循环是错误的唯一一次是在不确定的行为时,编译器决定您的程序不妨继续进行循环。
tl; dr不要使用普通的bool
和volatile
进行多线程。使用std::atomic<bool>
。
†并非在所有情况下。void bar(A& a) { a.work(); }
不适合某些版本。
‡实际上,围绕此问题进行了一些争论。
现在,我猜允许编译器优化(工作)while(true)
可能,是的。但是,只有它可以证明processSomeJob()
不会修改working
变量,即是否可以证明循环是无限的。
如果不是这种情况,那将意味着这样的事情效率很低……因为必须加载某些hotherclassmember的值
您的推理是正确的。但是,内存位置可能保留在缓存中,并且从CPU缓存中读取不一定会显着慢。如果doSomething
足够复杂,可以导致someOtherClassMember
从缓存中驱逐,则确保我们必须从内存加载,但另一方面,doSomething
可能是如此复杂,以至于单个内存负载相比无关紧要。
这两种情况中的哪一个?
。优化器将无法分析所有可能的代码路径;我们不能假设在所有情况下都可以优化循环。但是,如果证明someOtherClassMember
在任何代码路径中都没有修改,那么从理论上证明这是可能的,因此可以在理论上优化循环。
,但我也发现说[挥发性]应该在我的场景中使用。
volatile
在这里对您没有帮助。如果在另一个线程中修改了working
,则有一个数据竞赛。数据竞赛意味着程序的行为是未定义的。
要避免进行数据竞赛,您需要同步:使用MUTEX或原子操作来共享跨线程的访问。
Volatile
将在每次检查中重新加载working
变量。实际上,这通常会允许您使用由异步信号处理程序或其他线程制成的stopWorking
的调用来停止工作功能,但是根据标准,它不够。该标准需要或Sighandler&lt; ->常规上下文通信的volatile sig_atomic_t
的变量和 Anomics 用于线程间的通信。
- 空基优化子对象的地址
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 返回值优化:显式移动还是隐式
- 人脸跟踪arduino代码的优化
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 纯函数,为什么没有优化
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 如何以优化的方式同时迭代两个间距不相等的数组
- 小字符串优化(调试与发布模式)
- 浮点定向舍入和优化
- Visual Studio 调试优化如何工作?
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 线性优化目标函数中的绝对值
- GCC 会优化内联访问器吗?
- gcc 如何优化此循环?
- 如何防止 CUDA-GDB 中的<优化输出>值
- 为什么我的程序在 O0 和 O2 的优化级别返回不同的结果
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- 使用 std::p air 进行返回值优化