1.我在项目libuv 1.3.0中发现了这些代码。但我不明白为什么需要内存屏障(编译器)。
static int uv__async_make_pending(int* pending) {
/* Do a cheap read first. */
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
/* Micro-optimization: use atomic memory operations to detect if we've been
* preempted by another thread and don't have to make an expensive syscall.
* This speeds up the heavily contended case by about 1-2% and has little
* if any impact on the non-contended case.
*
* Use XCHG instead of the CMPXCHG that __sync_val_compare_and_swap() emits
* on x86, it's about 4x faster. It probably makes zero difference in the
* grand scheme of things but I'm OCD enough not to let this one pass.
*/
#if defined(__i386__) || defined(__x86_64__)
{
unsigned int val = 1;
__asm__ __volatile__ ("xchgl %0, %1"
: "+r" (val)
: "m" (*pending));
return val != 0;
}
#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 0)
return __sync_val_compare_and_swap(pending, 0, 1) != 0;
#else
ACCESS_ONCE(int, *pending) = 1;
return 0;
#endif
}
1.我们可以通过不同的环境来简化这些代码。对于x86:
static int uv__async_make_pending(int* pending) {
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
{
unsigned int val = 1;
__asm__ __volatile__ ("xchgl %0, %1"
: "+r" (val)
: "m" (*pending));
return val != 0;
}
}
对于GNU C 4:
static int uv__async_make_pending(int* pending) {
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
return __sync_val_compare_and_swap(pending, 0, 1) != 0;
}
对于其他人:
static int uv__async_make_pending(int* pending) {
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
ACCESS_ONCE(int, *pending) = 1;
return 0;
}
1.我用最后一个作为例子。如果我移除它的记忆屏障:
static int uv__async_make_pending(int* pending) {
if (*pending != 0)
return 1;
*pending = 1;
return 0;
}
上面的代码在gcc编译后会不会做错事?我不知道gcc会做什么优化。
1.我已经谷歌的代码直接,但没有结果来解释为什么内存障碍是必要的。
1.我有谷歌的内存障碍,但没有结果来解释当我需要使用内存障碍的具体代码。我知道什么是记忆障碍。
1.我有古勒的锁免费编程在C语言,但没有结果来解释代码将如何优化编译器。
1条答案
按热度按时间xcitsw881#
@Jérôme Richard,@Peter Cordes感谢您的评论。我想我已经知道问题的答案了。这里的ACCESS_ONE(内存屏障)保证从内存中读取或写入变量挂起,而不是寄存器。由于我把注意力都放在了指令的重写上,而忽略了寄存器替代内存访问的优化。在错误的方向上越走越远。当另一个人陷入无限循环时,有人给一个简单的提示是很重要的。