还是说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. STM32F0xx_TIM输出PWM配置详细过程

    前言 前面我说过STM32的定时器功能很强大,今天就来总结一下它的另外一个“强大”功能:TIM的比较输出功能,输出可调PWM波形.直接调用函数接口“TIM2_CH1_PWM(uint32_t Freq ...

  2. IEnumerable 接口 实现foreach 遍历 实例

    额 为啥写着东西? 有次面试去,因为用到的时候特别少 所以没记住, 这个单词 怎么写! 经典的面试题: 能用foreach遍历访问的对象的要求? 答:  该类实现IEnumetable 接口   声明 ...

  3. Castle 集锦

    Castle Windsor http://www.cnblogs.com/RicCC/archive/2010/03/30/castle-windsor-ioc-di.html 官方配置说明(反正我 ...

  4. “Guess the number” game

    项目描述:https://class.coursera.org/interactivepython-004/human_grading/view/courses/972072/assessments/ ...

  5. 九度oj 1525 子串逆序打印

    原题链接:http://ac.jobdu.com/problem.php?pid=1525 字符串简单题,注意开有结尾有空格的情况否则pe or wa #include<algorithm> ...

  6. Android里面的命名规范

    前前后后接触安卓也有一段时间了,但是对于Android命名规范这块一直没有太注意过.导致有的时候写出来的代码,前后的风格根本不一样,今天在网上查了一下,正好对自己来说可以好好的总结一下. 首先在And ...

  7. ios多线程的几种创建方式以及基本使用

    1.第一种,NSThread方法,此方法使用很少! 2.NSOperation,面向对象的多线程技术 3. NSObject多线程技术 4.苹果推荐使用的GCD技术 5.GDC中的队列

  8. JVM规范小结

    JVM规范组成: 1. 字节码(ByteCode): 以Class或Interface为基本单位, 具有固定结构. 2. 指令集(InstructionSet): 每个指令用一个字节表示, 最多256 ...

  9. 解决sharepoint 2010 用户配置文件同步服务 正在启动

    用户配置文件同步服务一直显示“正在启动”,而且无法停止,如下办法可以停止这个服务: 在sharepoint power shell 中执行下面的命令: Get-spserviceinstance 获取 ...

  10. MYSQL与 R

    1. 配置MySQL ODBC必须先安装MySQL ODBC driver下载地址可以为:http://www.mysql.com/downloads/connector/odbc/ 2. 控制面板\ ...