源代码和Runtime时执行的代码很可能不一样,这是因为编译器、处理器常常会为了追求性能对改变执行顺序。然而改变顺序执行很危险,很有可能使得运行结果和预想的不一样,特别是当重排序共享变量时。

 从源代码到Runtime需要经过三步的重排序:

编译器重排序

 为了提高性能,在不改变单线程的执行结果下,可以改变语句执行顺序。

 比如尽可能的减少寄存器的读写次数,充分利用局部性。像下面这段代码这样,交替的读x、y,会导致寄存器频繁的交替存储x和y,最糟的情况下寄存器要存储3次x和3次y。如果能让x的一系列操作一块做完,y的一块做完,理想情况下寄存器只需要存储1次x和1次y。

//优化前
int x = 1;
int y = 2;
int a1 = x * 1;
int b1 = y * 1;
int a2 = x * 2;
int b2 = y * 2;
int a3 = x * 3;
int b3 = y * 3; //优化后
int x = 1;
int y = 2;
int a1 = x * 1;
int a2 = x * 2;
int a3 = x * 3;
int b1 = y * 1;
int b2 = y * 2;
int b3 = y * 3;

指令重排序

 指令重排序是处理器层面做的优化。处理器在执行时往往会因为一些限制而等待,如访存的地址不在cache中发生miss,这时就需要到内存甚至外存去取,然而内存和外区的读取速度比CPU执行速度慢得多。

 早期处理器是顺序执行(in-order execution)的,在内存、外存读取数据这段时间,处理器就一直处于等待状态。现在处理器一般都是乱序执行(out-of-order execution),处理器会在等待数据的时候去执行其他已准备好的操作,不会让处理器一直等待。

 满足乱序执行的条件:

  1. 该缓存的操作数缓存好
  2. 有空闲的执行单元

 对于下面这段汇编代码,操作1如果发生cache miss,则需要等待读取内存外存。看看有没有能优先执行的指令,操作2依赖于操作1,不能被优先执行,操作3不依赖1和2,所以能优先执行操作3。

所以实际执行顺序是3>1>2

LDR R1, [R0];//操作1
ADD R2, R1, R1;//操作2
ADD R3, R4, R4;//操作3

内存系统重排序

 由于处理器有读、写缓存区,写缓存区没有及时刷新到内存,造成其他处理器读到的值不是最新的,使得处理器执行的读写操作与内存上反应出的顺序不一致

 如下面这个例子,可能造成处理器A读到的b=0,处理器B读到的a=0。A1写a=1先写到处理器A的写缓存区中,此时内存中a=0。如果这时处理器B从内存中读a,读到的将是0。

 以处理器A来说,处理器A执行的顺序是A1>A2>A3,但是由于写缓存区没有及时刷新到内存,所以实际顺序为A2>A1>A3。

初始化:
a = 0;
b = 0; 处理器A执行
a = 1; //A1
read(b); //A2 处理器B执行
b = 2; //B1
read(a); //B2

阻止重排序

 不论哪种重排序都可能造成共享变量中线程间不可见,这会改变程序运行结果。所以需要禁止对那些要求可见的共享变量重排序。

  • 阻止编译重排序:禁止编译器在某些时候重排序。
  • 阻止指令重排序和内存系统重排序:使用内存屏障或Lock前缀指令

从源代码到Runtime发生的重排序的更多相关文章

  1. Java并发编程原理与实战四十一:重排序 和 happens-before

    一.概念理解 首先我们先来了解一下什么是重排序:重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段. 从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序,如下图 ...

  2. 指令重排序及Happens-before法则随笔

    指令重排序 对主存的一次访问一般花费硬件的数百次时钟周期.处理器通过缓存(caching)能够从数量级上降低内存延迟的成本这些缓存为了性能重新排列待定内存操作的顺序.也就是说,程序的读写操作不一定会按 ...

  3. 多线程学习:Volatile与Synchronized的区别、什么是重排序

    java线程的内存模型 java的线程内存模型中定义了每个线程都有一份自己的共享变量副本(本地内存),里面存放自己私有的数据,其他线程不能直接访问,而一些共享变量则存在主内存中,供所有线程访问. 上图 ...

  4. 深入浅出Java并发包—指令重排序

    前面大致提到了JDK中的一些个原子类,也提到原子类是并发的基础,更提到所谓的线程安全,其实这些类或者并发包中的这么一些类,都是为了保证系统在运行时是线程安全的,那到底怎么样才算是线程安全呢? Java ...

  5. JVM并发机制的探讨——内存模型、内存可见性和指令重排序

    并发本来就是个有意思的问题,尤其是现在又流行这么一句话:“高帅富加机器,穷矮搓搞优化”. 从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多台机器或者多个CP ...

  6. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...

  7. Jvm 中的 重排序、主存、原子操作

    一.重排序 好处:重排序可以提升性能,避免在一个耗时很长的指令在“执行”阶段呆很长时间,而导致后续的指令都卡在“执行”之前的阶段上. 坏处:重排序对多线程的影响 class ReorderExampl ...

  8. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则[转]

    在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: ...

  9. JMM中的重排序及内存屏障

    目录 1. 概述 2. 重排序 2-1. as-if-serial语义 2-2. 重排序的种类 2-3. 从Java源代码到最终实际执行的指令序列, 会分别经历下面3中重排序. 3. 内存屏障类型 3 ...

随机推荐

  1. Codeforces 376C. Socks

    C. Socks time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...

  2. lynx---CentOS终端访问IP

    1.官网 http://lynx.isc.org 2.稳定版本 http://invisible-mirror.net/archives/lynx/tarballs/lynx2.8.8rel.2.ta ...

  3. ligerUI---ligerGrid中treegrid(表格树)的使用

    写在前面: 表格树是在普通ligerGrid的基础上,做了一点改变,使数据以表格树的形式显示出来,适用于有级别的数据比如菜单(有父菜单,父菜单下面有子菜单).表格树的显示有两种方法,可以根据自己的项目 ...

  4. 数据结构--汉诺塔递归Java实现

    /*汉诺塔递归 * 1.将编号0-N-1个圆盘,从A塔座移动到B上面 * 2.将编号N的1个圆盘,从A移动到C上面 * 3.最后将B上面的N-1个圆盘移动到C上面 * 注意:盘子的编号从上到下1-N ...

  5. JavaScript系列----面向对象的JavaScript(1)

    1.面向对象的编程 1.1.什么是面向对象编程 面向对象编程:即是把能够完成独立完成一部分功能的代码封装在一起,组成一个类. 举个例子来说: 这里有一把枪, 枪的种类很多,有步枪,机关枪,阻击枪... ...

  6. Three ways to throw exception in C#. Which is your preference?

    There are three ways to 'throw' a exception in C#  C#中有三种抛出异常的方式 Use the throw keyword without an id ...

  7. Java 核心内容相关面试题【4】

    spingmvc 和 structs的区别 我们用struts2时采用的传统的配置文件的方式,并没有使用传说中的0配置. spring3 mvc可以认为已经100%零配置了(除了配置spring mv ...

  8. Python ---------- Tensorflow (二)学习率

    假设最小化函数 y = x2 , 选择初始点 x0= 5 1. 学习率为1的时候,x在5和-5之间震荡. #学习率为1 import tensorflow as tf training_steps = ...

  9. 手动安装Eclipse的PyDev插件,重启无效

    想好好学习Python,又不想只用Emeditor开发,于是想到了Eclipse.之前配置过PyDev,很久没用,就放下了.这次重新配置,遇到了不少问题总结如下: 第一,使用网址自动更新.从网上搜了很 ...

  10. red5 自定义文件存放目录

    Red5 流媒体服务器 自定义文件存放目录 Red5在正常情况下,安装之后文件必须存放在Red5安装目录下的oflaDemo\streams中,不能自定义存放目录,例如Red5 安装在C盘,但是我的文 ...