重排序

一、重排序。

1、为什么需要重排序?

   现在的CPU一般采用流水线来执行指令。一个指令的执行被分成:取指、译码、访存、执行、写回、等若干个阶段。然后,多条指令可以同时存在于流水线中,同时被执行。

指令流水线并不是串行的,并不会因为一个耗时很长的指令在“执行”阶段呆很长时间,而导致后续的指令都卡在“执行”之前的阶段上。我们编写的程序都要经过优化后(编译器和处理器会对我们的程序进行优化以提高运行效率)才会被运行,优化分为很多种,其中有一种优化叫做重排序,重排序需要遵守as-if-serial规则和happens-before规则,不能说你想怎么排就怎么排,如果那样岂不是乱了套。重排序的目的是为了性能。

Example:
理想情况下:
过程A:cpu0—写入1—> bank0;
过程B:cpu0—写入2—> bank1;
如果bank0状态为busy, 则A过程需要等待
如果进行重排序,则直接可以先执行B过程。

2、重排序分类。

在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。

  1. 编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
  2. 指令级并行的重排序:如果不存l在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  3. 内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

但是,可以通过插入特定类型的Memory Barrier来禁止特定类型的编译器重排序和处理器重排序。

3、重排序过程。

一个好的内存模型实际上会放松对处理器和编译器规则的束缚,也就是说软件技术和硬件技术都为同一个目标而进行奋斗:在不改变程序执行结果的前提下,尽可能提高并行度。JMM对底层尽量减少约束,使其能够发挥自身优势。因此,在执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排序。一般重排序可以分为如下三种:

 
从源码到最终执行的指令序列的示意图
  1. 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
  2. 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序
  3. 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行的。

如图,1属于编译器重排序,而2和3统称为处理器重排序。这些重排序会导致线程安全的问题,一个很经典的例子就是DCL问题。针对编译器重排序,JMM的编译器重排序规则会禁止一些特定类型的编译器重排序针对处理器重排序,编译器在生成指令序列的时候会通过插入内存屏障指令来禁止某些特殊的处理器重排序

那么什么情况下,不能进行重排序了?下面就来说说数据依赖性。有如下代码:

double pi = 3.14 //A

double r = 1.0 //B

double area = pi * r * r //C

这是一个计算圆面积的代码,由于A,B之间没有任何关系,对最终结果也不会存在关系,它们之间执行顺序可以重排序。因此可以执行顺序可以是A->B->C或者B->A->C执行最终结果都是3.14,即A和B之间没有数据依赖性。具体的定义为:如果两个操作访问同一个变量,且这两个操作有一个为写操作,此时这两个操作就存在数据依赖性这里就存在三种情况:1. 读后写;2.写后写;3. 写后读,者三种操作都是存在数据依赖性的,如果重排序会对最终执行结果会存在影响。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序。

4、重排序对多线程的影响。

class ReorderExample { int a = 0; boolean flag = false; public void writer() { a = 1; //1 flag = true; //2 } Public void reader() { if (flag) { //3 int i = a * a; //4 …… } } } 

flag为标志位,表示a有没有被写入,当A线程执行 writer 方法,B线程执行 reader 方法,线程B在执行4操作的时候,能否看到线程A对a的写入操作?

答案是: 不一定!

由于操作1和操作2没有数据依赖关系,编译器和处理器可以对这两个操作重排序。

如果操作1和操作2做了重排序,程序执行时,线程A首先写标记变量 flag,随后线程 B 读这个变量。由于条件判断为真,线程 B 将读取变量a。此时,变量 a 还根本没有被线程 A 写入,在这里多线程程序的语义被重排序破坏了!

 

二、数据依赖性。

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:

名称代码示例说明
写后读a = 1;b = a;写一个变量之后,再读这个位置。
写后写a = 1;a = 2;写一个变量之后,再写这个变量。
读后写a = b;b = 1;读一个变量之后,再写这个变量。

上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。前面提到过,编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。注意,这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。所以有数据依赖性的语句不能进行重排序。

三、as-if-serial规则。

as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义。

为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。为了具体说明,请看下面计算圆面积的代码示例:

double pi  = 3.14;    //A
double r   = 1.0;     //B
double area = pi * r * r; //C

上面三个操作的数据依赖关系如下图所示:

如上图所示,A和C之间存在数据依赖关系,同时B和C之间也存在数据依赖关系。因此在最终执行的指令序列中,C不能被重排序到A和B的前面(C排到A和B的前面,程序的结果将会被改变)。但A和B之间没有数据依赖关系,编译器和处理器可以重排序A和B之间的执行顺序。as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器,runtime 和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的as-if-serial语义使单线程程序员无需担心重排序会干扰他们,也无需担心内存可见性问题

四、happens-before规则。

 

具体的一共有六项规则:

1、程序顺序规则。(可见性)

一个线程中的每个操作,happens-before于该线程中的任意后续操作。程序顺序规则中所说的每个操作happens-before于该线程中的任意后续操作并不是说前一个操作必须要在后一个操作之前执行,而是指前一个操作的执行结果必须对后一个操作可见,如果不满足这个要求那就不允许这两个操作进行重排序。

2、监视器锁规则。

对一个锁的解锁,happens-before于随后对这个锁的加锁。

3、volatile变量规则。

对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

4、传递性。

如果A happens-before B,且B happens-before C,那么A happens-before C。

5、start()规则。

如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。

6、join()规则。

如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

7、程序中断规则。

对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。

8、对象finalize规则。

一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。

 

 

转载于:https://www.cnblogs.com/igoodful/p/9473253.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tableau三轴该怎么做_如何用tableau绘制城市地铁线路图?

在用tableau绘制地铁线路图之前,当然是要获取相关的数据啦我们以郑州目前已开通的地铁为例,分别是1、2、5号线经度、维度可在 网页上自行搜索哦(以谷歌地图为准)有了这些下面我们就要开始啦将Excel中你所需要的数据直接导入到tabl…

JS七种加密解密方法

HTML或JS加密解密 本文一共介绍了七种方法:   一:最简单的加密解密   二:转义字符"\"的妙用   三:使用Microsoft出品的脚本编码器Script Encoder来进行编码 (自创简单解码)  …

提高solr的搜索速度

之前是使用12台机分布式搜索,1台为主机做索引并分发给子机,8台做大索引搜索服务,3 台做小索引搜索服务,配置基本是内存在4-8G,cpu:2-8core的服务器,索引的大小为8G。搜索的响应时间 是150ms左右。&#xff…

哲学到编程:思想的实例化

万古长江水,千年儒释道。历史的长流中,芸芸众生,参差不齐,但总是能够总结出一个“生旦净末丑”来。儒、释、道,五千年的中华文化,却总是围绕着这三种主流思想交相演绎。千年间,豪士俊杰&#xf…

python 字符串交集_Python序列--集合(set)

集合集合用于保存不重复元素。- 集合和列表非常相似- 不同点:1.集合中只能存储不可变对象2.集合中存储的对象是无序(不是按照元素的插入顺序保存)3.集合中不能出现重复的元素集合的所有元素都放在一对”{ }” 中,两个相邻的元素之间用”,”分隔。集合最好…

mysql binlog日志查看及解码

mysql bin log日志导出 mysqlbinlog mysql-bin.000005 > /home/17bin.log 需要添加参数(--base64-outputdecode-rows -v)对输出结果解码 mysqlbinlog --base64-outputdecode-rows -v mysql-bin.000005 > /home/17bin.log转载于:https://www.cnbl…

【Python开发】Python的GUI用法总结

引用模块(tkinter): 1 from tkinter import * 主窗口设置: 1 # 主窗口 2 tk Tk() # 主窗口实例化 3 tk.title("文本处理工具") # 主窗口标题 4 tk.geometry("700x4001001…

JAVA 环境变量配置

JAVA 环境变量配置 1. 安装JDK 2.配置系统变量 新建          JAVA_HOME:D:\Program Files\Java\jdk1.8.0_65 Path添加       %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 新建CLASSPATH  .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 3.完成…

8修改host_正点原子【STM32-F407探索者】第五十九章 USB 鼠标键盘(Host)实验

1)资料下载:点击资料即可下载2)对正点原子Linux感兴趣的同学可以加群讨论:9354467413)关注正点原子公众号,获取最新资料更新上一章我们向大家介绍了如何利用 STM32F4 的 USB HOST 接口来驱动 U 盘,本章,我们 将利用 ST…

CF815C Karen and Supermarket [树形DP]

题目传送门 Karen and Supermarket On the way home, Karen decided to stop by the supermarket to buy some groceries. She needs to buy a lot of goods, but since she is a student her budget is still quite limited. In fact, she can only spend up to b dollars. Th…

linux命令积累之egrep命令

学搭建Nginx环境,必须要配置的Nginx.conf文件中,如下:#user nobody;worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events { worke…

Sublime Text 3 安装及插件推荐

本篇介绍跨平台编辑器Sublime Text 3的安装和其插件推荐。 目录: 1.介绍 2.下载安装 3.插件 4.参考资料 1.介绍 Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定&#xff0c…

6工程文件夹作用_data_dragon数据工程小工具收集

最近在GitHub上创建了一个新工程,收集个人在数据工程工作的小工具集合,命名为data_dragon (数据一条龙)。取这个名字的是希望这些脚本或代码能够复用,端到端地减少临时数据处理的时间。最近因为工作上的一些变化,写作节奏有点被打…

暑假第十七测

题解&#xff1a; 第一题 #include<bits/stdc.h> using namespace std; #define ll long long const int M 1e5 10; ll a[M], b[M], ans; priority_queue <ll, vector<ll> , greater<ll> > Q; int main(){freopen("buy.in","r",…

Uva 11354 LCA 倍增祖先

题目链接&#xff1a;https://vjudge.net/contest/144221#problem/B 题意&#xff1a;找一条从 s 到 t 的路&#xff0c;使得瓶颈路最小。 点的数目是10^4&#xff0c;如果向之前的方案求 maxcost数组&#xff0c;O(n*n)时间是过不了的&#xff0c;这个时候&#xff0c;用到了…

Nginx搭建flv视频点播服务器

Nginx搭建flv视频点播服务器前一段时间使用Nginx搭建的多媒体服务器只能在缓冲过的时间区域内拖放, 而不能拖放到未缓冲的地方. 这就带来了一个问题: 如果视频限速的速率很小, 那么客户端观看视频时肯定不流畅, 而且用户不能向前拖放, 用户体验很不好. 如果视频限速的速率很大或…

编码拾遗

1 #!/usr/bin/env python32 #-*- coding:utf-8 -*-3 4 Administrator 5 2018/8/16 6 7 8 # fopen("demo","r",encoding"utf8")9 # dataf.read() 10 # print(data) 11 # f.close() 12 13 14 # print("沈哲子") 15 16 s"中国&qu…

Xcode:Foundation框架找不到,或者是自动提示出现问题

问题描述&#xff1a;Foundation框架找不到&#xff0c;或者是自动提示出现问题 之前的操作&#xff1a;手贱&#xff0c;不少心把编译器里面的源码改了处理办法&#xff1a;清理缓存缓存位置&#xff1a;点击桌面后&#xff0c;选择系统菜单栏&#xff1a;前往—电脑—硬盘—用…

mybatis 不生效 参数_Mybatis-日志配置

日志Mybatis 的内置日志工厂提供日志功能&#xff0c;内置日志工厂将日志交给以下其中一种工具作代理&#xff1a;SLF4JApache Commons LoggingLog4j 2Log4jJDK loggingMyBatis 内置日志工厂基于运行时自省机制选择合适的日志工具。它会使用第一个查找得到的工具(按上文列举的顺…

PS通过滤色实现简单的图片拼合

素材如下&#xff1a; 素材一&#xff1a; 雪山 素材二&#xff1a; 月亮 效果&#xff1a; 实现步骤 1、在PS中打开雪山素材一 2、将月亮素材直接拖入雪山所在的图层中 3、锁定置入素材的高宽比&#xff08;点击一下链状按钮&#xff09; 4、调整月亮到合适大小合适位置 5、…