还是说Memory Model,gcc的__sync_synchronize真是太坑爹了

还是说Memory Model,gcc的__sync_synchronize真是太坑爹了!

时间 2012-01-29 03:18:35  IT牛人博客聚合网站

原文  http://www.udpwork.com/item/6751.html

主题 GCC

嗯,还是说可见性的问题。由于CPU和编译器的乱序执行功能,我们经常不得不在代码中手动插入memory barrier。如果你还不清楚memory barrier是什么,那么请先读这个 http://en.wikipedia.org/wiki/Memory_barrier

假如你已经了解它了,那么具体怎么用呢?怎么在代码中插入一个memory barrier ? 用哪个函数?

gcc的手册中有一节叫做”Built-in functions for atomic memory access”,然后里面列举了这样一个函数:

__sync_synchronize (…)

This builtin issues a full memory barrier.

来,我们写段代码试下:

?  C

int main(){
  __sync_synchronize();
  return 0;
}

然后用gcc4.2编译,

# gcc -S -c test.c

然后看对应的汇编代码,

?  ASM

main:
  pushq %rbp
  movq %rsp, %rbp
  movl $0, %eax
  leave
  ret

嗯?Nothing at all !!! 不信你试一试,我的编译环境是Freebsd 9.0 release, gcc (GCC) 4.2.1 20070831 patched [FreeBSD]。

好,我换个高版本的gcc编译器试一试,gcc46 (FreeBSD Ports Collection) 4.6.3 20120113 (prerelease)

?  ASM

main:
 
pushq %rbp
 
movq %rsp, %rbp
 
mfence
 
movl $0, %eax
 
popq %rbp
 
ret

看,多了一行,mfence。

怎么回事呢?这是gcc之前的一个BUG:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36793 。 2008年被发现,然后修复的。其实它之所以是一个BUG,关键在于gcc的开发者很迷惑,mfence在x86 CPU上到底有没有用?有嘛用?

那么mfence到底能不能提供我们想要的结果呢?

Intel的手册中有这么一段我一直没太读懂:

“Processors are free to fetch and cache data speculatively from regions of system memory that use the WB, WC, and WT memory types. This speculative fetching can occur at any time and is not tied to instruction execution. Thus, it is not ordered with respect to executions of the MFENCE instruction; data can be brought into the caches speculatively just before, during, or after the execution of an MFENCE instruction. Processors are free to fetch and cache data speculatively from regions of system memory that use the WB, WC, and WT memory types. This speculative fetching can occur at any time and is not tied to instruction execution. Thus, it is not ordered with respect to executions of the MFENCE instruction; data can be brought into the caches speculatively just before, during, or after the execution of an MFENCE instruction.”

但是在关于Memory Ordering的章节又说:

“Reads cannot pass earlier MFENCE instructions

Writes cannot pass earlier MFENCE instructions.

MFENCE instructions cannot pass earlier reads or writes”

综合起来的意思是,如果代码是这样,

READ A 
MFENCE 
READ B

那么可能会成为这样

READ A 
Speculative READ B 
MFENCE

但是不会成为这样

Speculative READ B 
READ A 
MFENCE

也就是说,Speculative READ可以穿越mfence,但是无法走的更远了,不能再穿越前面的读写的操作。所以无论如何,A、B的读取顺序还是被严格保持的。不知道我的理解对不对。

但是这些只是针对单CPU而言。多CPU(包括超线程和多核)的时候,如果还是用Locked instructions,那么没问题。否则手册里的规则没有特别提到mfence,而是说了这么一句,”Memory ordering obeys causality (memory ordering respects transitive visibility).” Causality 也是一种relaxed Model,我不是很能理解。只看到一句通俗点的解释,” If i see it and tell you about it , then you will see it too.”

这个改动对JAVA社区的影响巨大。JVM通过插入mfence指令,使得volatile变量的写入操作,能得到sequential consistency。于是Java社区现在就只有两条路,要么修改语言规范,要么把mfence换成代价更高的xchg指令。David Dice(transactional memory的TL/TL2算法的发明人)的这篇日志http://blogs.oracle.com/dave/entry/java_memory_model_concerns_on 那写的是相当的悲苦啊。

如果实在想在代码中使用membar,那么最好是认准某个编译器和某个平台。否则,非常推荐intel tbb。tbb:: atomic_fence()。

嗯,最后说下C/C++中的volatile。如果非要去咬文嚼字的看标准,那么这个关键字对于多线程同步简直一点帮助都没有。那么实际呢?我们发现很多人在C/C++中用这个关键字,而且,他们不是在写驱动,就是普通的用户态的代码。他们真的都错了吗?

看看这段神奇的代码:

http://software.intel.com/en-us/articles/single-producer-single-consumer-queue/

这段代码中,所有的membar都可以去掉,无非是告诉编译器,别乱搞。volatile关键字,在具体某个编译器中的含义,要远比ISO标准中说的要多。

这是我今天用tbb写的一个Singleton:

?  CPP

class StatusCodeManager{
public:
  static StatusCodeManager& instance(){
    if( !value ) {
      StatusCodeManager* tmp = new StatusCodeManager();
      if( value.compare_and_swap(tmp,NULL)!=NULL )
        // Another thread installed the value, so throw away mine.
        delete tmp;
    }
 
    return *value;
  }
protected:
  StatusCodeManager();
private:
  static tbb::atomic<StatusCodeManager*> value;
};

虽然无锁化了,但是并不保证构造函数只执行一次。所以我一直在愁,value->init();插在哪里好呢?

(p.s. 今天找paper的时候看到一篇让我满眼冒金星的神文:《Mathematizing C++ Concurrency》 http://www.cl.cam.ac.uk/~pes20/cpp/popl085ap-sewell.pdf 第一作者是剑桥的某在读博士。)

时间: 2024-10-01 07:48:38

还是说Memory Model,gcc的__sync_synchronize真是太坑爹了的相关文章

java学习:JMM(java memory model)、volatile、synchronized、AtomicXXX理解

一.JMM(java memory model)内存模型 从网上淘来二张图: 上面这张图说的是,在多核CPU的系统中,每个核CPU自带高速缓存,然后计算机主板上也有一块内存-称为主内(即:内存条).工作时,CPU的高速缓存中的数据通过一系列手段来保证与主内的数据一致(CacheCoherence),更直白点,高速缓存要从主内中load数据,处理完以后,还要save回主存. 上图说的是,java中的各种变量(variable)保存在主存中,然后每个线程自己也有自己的工作内存区(working me

C# Memory model

Memory Model http://blogs.msdn.com/b/cbrumme/archive/2003/05/17/51445.aspx C# - The C# Memory Model in Theory and Practice https://msdn.microsoft.com/magazine/jj863136 https://msdn.microsoft.com/en-us/magazine/jj883956.aspx

CUDA ---- Memory Model

Memory kernel性能高低是不能单纯的从warp的执行上来解释的.比如之前博文涉及到的,将block的维度设置为warp大小的一半会导致load efficiency降低,这个问题无法用warp的调度或者并行性来解释.根本原因是获取global memory的方式很差劲. 众所周知,memory的操作在讲求效率的语言中占有极重的地位.low-latency和high-bandwidth是高性能的理想情况.但是购买拥有大容量,高性能的memory是不现实的,或者不经济的.因此,我们就要尽量

#define barrier() __asm__ __volatile__(&quot;&quot;: : :&quot;memory&quot;) 中的memory是gcc的东西

gcc内嵌汇编简介 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可, GCC会自动插入代码完成必要的操作. 1.简单的内嵌汇编 例: __asm__ __volatile__("hlt"); "__asm__"表示后面的代码为内嵌汇编,"asm"是"__asm__"的别名.&

Deep Analysis Java Memory Model

提纲: •Java内存模型 •volatile关键字 •long和double变量的特殊规则 •原子性,可见性与有序性 •先行发生原则 •Java与线程 1.java内存模型 Java虚拟机规范中试图定义一种java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台上都能达到一致的内存访问效果. 主内存与工作内存: JMM的主要目标是定义了程序中各个变量的访问规则,及虚拟机中将变量存储到内存和从内存中取出变量的底层细节.

Keil中Memory Model和Code Rom Size说明

源:Keil中Memory Model和Code Rom Size说明 C51中定义变量时如果省略存储器类型,Keil C51编译系统则会按编译模式SMALL.COMPACT和LARGE所规定的默认存储器类型去指定变量的存储区域,无论什么存储模式都可以声明变量在任何的8051存储区范围i,但是把最常用的命令如循环计数器和队列索引放在内部数据区可以显著地提高系统性能.以下介绍一下Keil编译选项Target中的Memory Model和Code Rom Size的设置. Memory Model(

memory model

最近看C++11 atomic发现对memory_order很是不理解,memory_order_relaxed/memory_order_consume/memory_order_acquire/memory_order_release/memory_order_acq_rel/ memory_order_seq_cst.这些都是跟memory model有关 关于memory model,对于线程来说,其实是跟编译器相关的.因为我们的编译器在把C++语言翻译成机器代码的时候,会进行各种优化.

当我们在谈论JMM(Java memory model)的时候,我们在谈论些什么

前面几篇中,我们谈论了synchronized.final以及voilate的用法和底层实现,都绕不开一个话题-Java内存模型(java memory model,简称JMM).Java内存模型是保证线程安全的基础,主要描述了程序中全序的同步动作在不同线程访问共享全局变量时所体现的原子性.可见性和有序性上的限制. 1.定义 维基百科定义:The Java memory model describes how threads in the Java programming language in

死磕 java同步系列之JMM(Java Memory Model)

简介 Java内存模型是在硬件内存模型上的更高层的抽象,它屏蔽了各种硬件和操作系统访问的差异性,保证了Java程序在各种平台下对内存的访问都能达到一致的效果. 硬件内存模型 在正式讲解Java的内存模型之前,我们有必要先了解一下硬件层面的一些东西. 在现代计算机的硬件体系中,CPU的运算速度是非常快的,远远高于它从存储介质读取数据的速度,这里的存储介质有很多,比如磁盘.光盘.网卡.内存等,这些存储介质有一个很明显的特点--距离CPU越近的存储介质往往越小越贵越快,距离CPU越远的存储介质往往越大