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

时间 2012-01-29 03:18:35  IT牛人博客聚合网站
主题 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 第一作者是剑桥的某在读博士。)

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

  1. Java (JVM) Memory Model – Memory Management in Java

    原文地址:http://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java Understanding JV ...

  2. memory model

    最近看C++11 atomic发现对memory_order很是不理解,memory_order_relaxed/memory_order_consume/memory_order_acquire/m ...

  3. Keil中Memory Model和Code Rom Size说明

    C51中定义变量时如果省略存储器类型,Keil C51编译系统则会按编译模式SMALL.COMPACT和LARGE所规定的默认存储器类型去指定变量的存储区域,无论什么存储模式都可以声明变量在任何的80 ...

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

    前面几篇中,我们谈论了synchronized.final以及voilate的用法和底层实现,都绕不开一个话题-Java内存模型(java memory model,简称JMM).Java内存模型是保 ...

  5. 【翻译】go memory model

    https://studygolang.com/articles/819 原文链接 Introduction The Go memory model specifies the conditions ...

  6. 并发研究之Java内存模型(Java Memory Model)

    Java内存模型JMM java内存模型定义 上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题.那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬 ...

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

    一.JMM(java memory model)内存模型 从网上淘来二张图: 上面这张图说的是,在多核CPU的系统中,每个核CPU自带高速缓存,然后计算机主板上也有一块内存-称为主内(即:内存条).工 ...

  8. CUDA ---- Memory Model

    Memory kernel性能高低是不能单纯的从warp的执行上来解释的.比如之前博文涉及到的,将block的维度设置为warp大小的一半会导致load efficiency降低,这个问题无法用war ...

  9. 11 The Go Memory Model go语言内置模型

    The Go Memory Model go语言内置模型 Version of May 31, 2014 Introduction 介绍 Advice 建议 Happens Before 在发生之前 ...

随机推荐

  1. 最大子列和CT 01-复杂度2 Maximum Subsequence Sum

    Given a sequence of K integers { N​1​​, N​2​​, ..., N​K​​ }. A continuous subsequence is defined to ...

  2. 第一步 django的下载安装

    django是python众多web框架中比较有名的一个,以大包大揽功能俱全而著名.但作为重量级的web框架,难免性能上回有所损失,不过由于其封装了各种API,在开发的时候会便利许多.所以也是深受欢迎 ...

  3. openSUSE13.1无法打开Yast的安装/移除软件管理软件的解决办法·(未解决,临时方法) 收获:有问题,读日志

    看了日志发现错误在于Ruby,新的YaST是基于Ruby的,而我用rvm安了新版本Ruby,日志里这么出错: 2014-08-22 20:20:57 <3> linux-vfpp.site ...

  4. ThinkPHP 3.2.2跨控制器调用方法

     所谓跨控制器调用,指的是在一个控制器中调用另一个控制器的某个方法.在ThinkPHP中有三种方式实现跨控制器调用: 直接实例化: A()函数实例化; R()函数实例化. (1)直接实例化  直接实例 ...

  5. 安装RubyMine

    在mac上安装RubyMine的方法: 1.运行 brew cask install rubymine  自动安装. 2.按提示安装java更新. 3.RubyMine注册码: name: rubym ...

  6. 数据密集型 和 cpu密集型 event loop

    Node.js在官网上是这样定义的:“一个搭建在Chrome JavaScript运行时上的平台,用于构建高速.可伸缩的网络程序.Node.js采用的事件驱动.非阻塞I/O模型使它既轻量又高效,是构建 ...

  7. iOS下日期的处理

    NSDate存储的是世界标准时(UTC),输出时需要根据时区转换为本地时间 Dates         NSDate类提供了创建date,比较date以及计算两个date之间间隔的功能.Date对象是 ...

  8. bootstrap bootstrapTable 隐藏列

    主要代码: <script type="text/javascript"> $(function () { LoadingDataListOrderRealItems( ...

  9. Objective-C面向对象(三)

    1.类的继承 OC的继承是单继承,每个子类只有一个直接父类 1.1 继承的特点 OC继承的语法 @interface SubClass :SuperClass { //成员变量定义 } //方法定义部 ...

  10. haproxy实现mysql从库负载均衡

    本文主要讲述通过haproxy实现mysql从库间的负载均衡,至于mysql主从的搭建,本文不再重述,可以参考我之前写的博客. 1.首先下载haproxy包 wget http://haproxy.1 ...