编译器可以从全局变量读取两次,而不是存储局部变量吗? [英] Can a compiler read twice from a global variable, instead of storing a local one?

查看:59
本文介绍了编译器可以从全局变量读取两次,而不是存储局部变量吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在尝试重新熟悉多线程,并发现

I've been trying to get re-familiarized multi-threading recently and found this paper. One of the examples says to be careful when using code like this:

int my_counter = counter; // Read global
int (* my_func) (int);
if (my_counter > my_old_counter) {
  ... // Consume data
  my_func = ...;
  ... // Do some more consumer work
}
... // Do some other work
if (my_counter > my_old_counter) {
... my_func(...) ...
}

说明:

如果编译器决定需要溢出寄存器在两个测试之间包含我的计数器,它可能会决定避免存储值(毕竟这只是计数器的副本),并且而是简单地重新读取第二个计数器的值比较涉及我的柜台[...]

If the compiler decides that it needs to spill the register containing my counter between the two tests, it may well decide to avoid storing the value (it’s just a copy of counter, after all), and to instead simply re-read the value of counter for the second comparison involving my counter[...]

执行此操作会将代码转换为:

Doing this would turn the code into:

int my_counter = counter; // Read global
int (* my_func) (int);
if (my_counter > my_old_counter) {
  ... // Consume data
  my_func = ...;
  ... // Do some more consumer work
}
... // Do some other work
my_counter = counter; // Reread global!
if (my_counter > my_old_counter) {
... my_func(...) ...
}

但是,我对此持怀疑态度.我不明白为什么允许编译器执行此操作,因为据我了解,仅当尝试以任意数量的读取和至少一次写入操作访问同一内存区域时,才会发生数据争用.作者继续指出:

I, however, am skeptical about this. I don't understand why the compiler is allowed to do this, since to my understanding a data race only occurs when trying to access the same memory area with any number of reads and at least a write at the same time. The author goes on to motivate that:

核心问题来自编译器利用假设变量值不能异步更改而没有一个明确的任务

the core problem arises from the compiler taking advantage of the assumption that variable values cannot asynchronously change without an explicit assignment

在我看来,在这种情况下该条件得到了遵守,因为局部变量my_counter从未被访问过两次,其他线程也无法访问.编译器将如何知道全局变量不能由另一个线程在另一个转换单元中的其他位置设置?实际上,我不能假设第二种if情况实际上已经被优化掉了.

It seems to me that the condition is respected in this case, as the local variable my_counter is never accessed twice and cannot be accessed by other threads. How would the compiler know that the global variable can not be set elsewhere, in another translation unit by another thread? It cannot, and in fact, I assume that the second if case would just be actually optimized away.

作者错了,还是我错过了什么?

Is the author wrong, or am I missing something?

推荐答案

除非 counter 是显式的 volatile ,否则编译器可能会假设它在当前未更改的情况下不会更改.执行范围可能会改变它.这意味着如果变量上没有别名,或者在它们之间没有编译器不知道其效果的函数调用,则任何外部修改都是未定义的行为.使用 volatile ,即使编译器不知道如何,您也将尽可能声明外部更改.

Unless counter is explicitly volatile, the compiler may assume that it never changes if nothing in the current scope of execution could change it. That means if there can be either no alias on a variable, or there are no function calls in between for which the compiler can't know the effects, any external modification is undefined behavior. With volatile you would be declaring external changes as possible, even if the compiler can't know how.

这样的优化是完全有效的.实际上,即使它确实执行了复制操作,它也仍然不是线程安全的,因为该值可能会在读取过程中部分更改,或者甚至可能由于无法在没有同步原语或原子的情况下保证缓存一致性而完全过时.

So that optimization is perfectly valid. In fact, even if it did actually perform the copy it still wouldn't be threadsafe as the value may change partially mid read, or might even be completely stale as cache coherency is not guaranteed without synchronisation primitives or atomics.

好吧,实际上在x86上,至少在对齐时,您将不会获得整数的中间值.这是该体系结构的保证之一.过时的缓存仍然适用,该值可能已被另一个线程修改.

Well, actually on x86 you won't get an intermediate value for an integer, at least as long as it is aligned. That's one of the guarantees the architecture makes. Stale caches still apply, the value may already have been modified by another thread.

如果需要此行为,请使用互斥或​​原子.

Use either a mutex or an atomic if you need this behavior.

这篇关于编译器可以从全局变量读取两次,而不是存储局部变量吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆