首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么GCC不使用负载(无栅栏)和STORE+SFENCE的顺序一致性?

为什么GCC不使用负载(无栅栏)和STORE+SFENCE的顺序一致性?
EN

Stack Overflow用户
提问于 2013-09-27 09:29:47
回答 3查看 3.9K关注 0票数 16

以下是在x86/x86_64中实现顺序一致性的四种方法:

  1. 负荷(无栅栏)和STORE+MFENCE
  2. 负载(无栅栏)和锁XCHG
  3. MFENCE+LOAD和商店(无栅栏)
  4. 锁定XADD(0)并存储(无栅栏)

正如这里所写的:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

C/C++11操作x86实现

  • 加载Seq_Cst: MOV (从内存)
  • 存储Seq Cst:(锁) XCHG // alternative: MOV (进入内存),MFENCE

注意: C/C++11与x86之间有另一种映射,而不是锁定(或栅栏) Seq存储锁/栅栏-- Seq负载:

  • Load Seq_Cst: LOCK XADD(0) // alternative: MFENCE,MOV (来自内存)
  • 存储Seq Cst: MOV (进入内存)

GCC 4.8.2(GDB in x86_64)科技委采用第一(1)种方法,即荷载(无栅栏)和STORE+MFENCE:

代码语言:javascript
复制
std::atomic<int> a;
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
0x4613e8  <+0x0058>         mov    0x38(%rsp),%eax
0x4613ec  <+0x005c>         mov    %eax,0x20(%rsp)
0x4613f0  <+0x0060>         mfence

如我们所知,MFENCE = LFENCE+SFENCE。然后我们可以重写这段代码:LOAD(without fence) and STORE+LFENCE+SFENCE

问题:

  1. 为什么我们不需要在加载之前使用LFENCE,而需要在存储后使用LFENCE (因为LFENCE只有在加载之前才有意义!)?
  2. 为什么GCC不使用方法:加载(无栅栏)和STORE+SFENCE的std::memory_order_seq_cst?
EN

回答 3

Stack Overflow用户

发布于 2013-10-12 11:30:16

x86所做的唯一的重新排序(对于正常的内存访问)是,它可能会重新排序存储后的负载。

SFENCE保证在栅栏之前的所有商店在栅栏之后完成所有的商店。LFENCE保证在栅栏之前的所有负载都在栅栏之后完成。对于正常的内存访问,默认情况下已经提供了单个SFENCE或LFENCE操作的排序保证。基本上,LFENCE和SFENCE本身仅适用于x86较弱的内存访问模式。

无论是LFENCE、SFENCE还是LFENCE + SFENCE都不能防止存储后的负载被重新排序。是的。

相关参考是英特尔x86架构手册。

票数 7
EN

Stack Overflow用户

发布于 2013-09-30 07:12:50

考虑以下代码:

代码语言:javascript
复制
#include <atomic>
#include <cstring>

std::atomic<int> a;
char b[64];

void seq() {
  /*
    movl    $0, a(%rip)
    mfence
  */
  int temp = 0;
  a.store(temp, std::memory_order_seq_cst);
}

void rel() {
  /*
    movl    $0, a(%rip)
   */
  int temp = 0;
  a.store(temp, std::memory_order_relaxed);
}

对于原子变量"a",seq()和rel()在x86体系结构中都是有序的和原子的,因为:

  1. mov是一个原子指令。
  2. mov是一种遗留指令,Intel承诺为遗留指令提供有序内存语义,使其与总是使用有序内存语义的旧处理器兼容。

不需要栅栏将常量值存储到原子变量中。之所以存在栅栏,是因为std::memory_order_seq_cst意味着所有内存都是同步的,而不仅仅是保存原子变量的内存。

这种效果可以通过以下集合和get函数来演示:

代码语言:javascript
复制
void set(const char *s) {
  strcpy(b, s);
  int temp = 0;
  a.store(temp, std::memory_order_seq_cst);
}

const char *get() {
  int temp = 0;
  a.store(temp, std::memory_order_seq_cst);
  return b;
}

strcpy是一个库函数,如果在运行时可用,它可能使用更新的sse指令。由于sse指令在旧处理器中不可用,因此不需要向后兼容,内存顺序也没有定义。因此,一个线程中的strcpy的结果可能在其他线程中不可直接看到。

上面的set和get函数使用一个原子值来执行内存同步,以便strcpy的结果在其他线程中变得可见。现在,篱笆很重要,但是它们在调用原子::存储中的顺序并不重要,因为在原子::store中不需要内部的栅栏。

票数 6
EN

Stack Overflow用户

发布于 2015-09-22 02:09:44

SFENCE + LFENCE是而不是a StoreLoad屏障,所以问题的前提是不正确的。(还请参阅我从同一个用户为什么是(或者不是?)SFENCE + LFENCE相当于MFENCE?获得的关于同一问题的另一个版本的答案)

  • SFENCE可以传递(出现在)较早的负载。(这只是一个StoreStore屏障)。
  • LFENCE可以通过早期的商店。(负载不能在两个方向通过: LoadLoad屏障)。
  • 负载可以通过SFENCE (但是存储不能通过LFENCE,所以它既是一个LoadStore屏障,也是一个LoadLoad屏障)。

LFENCE+SFENCE不包括任何能阻止存储在稍后加载后才被缓冲的内容。MFENCE 可以通过来防止这种情况发生。

普林的博客文章用图表更详细地解释了StoreLoad障碍是如何特殊的,并有一个工作代码的实际示例,演示了不使用MFENCE的重新排序。任何对内存排序感到困惑的人都应该从这个博客开始。

x86有一个强记忆模型,每个普通存储都有发布语义,每个普通负载都有获取语义。这篇文章有详细信息

LFENCE和SFENCE 装载/储存,它们是弱有序的,并且绕过缓存。

如果这些链接死了,我的对另一个类似问题的回答中还有更多的信息。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19047327

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档