谈乱序执行和内存屏障

2023-10-28

10多年前的程序员对处理器乱序执行和内存屏障应该是很熟悉的,但随着计算机技术突飞猛进的发展,我们离底层原理越来越远,这并不是一件坏事,但在有些情况下了解一些底层原理有助于我们更好的工作,比如现代高级语言多提供了多线程并发技术,如果不深入下来,那么有些由多线程造成问题就很难排查和理解.

今天准备来聊聊乱序执行技术和内存屏障.为了能让大多数人理解,这里省略了很多不影响理解的旁枝末节,但由于我个人水平有限,如果不妥之处,希望各位指正.


按顺执行技术

在开始说乱序执行之前,得先把按序执行说一遍.在早期处理器中,处理器执行指令的顺序就是按照我们编写汇编代码的顺序执行的,换句话说此时处理器指令执行顺序和我们代码顺序一致,我们称之为按序执行(In Order Execution).我们以烧水泡茶为例来说明按序执行的过程(熟悉的同学会想起华罗庚的统筹学):

  1. 洗水壶
  2. 烧开水
  3. 洗茶壶
  4. 洗茶杯
  5. 拿茶叶
  6. 泡茶

我们假设每一步代表一条指令的执行,此时从指令1到指令6执行的过程就是我们所说的按序执行.整个过程可以表示为:

 

这里写图片描述

按序执行对于早期处理器而言是一种行之有效的方案,但随着对时间的要求,我们希望上述过程能够在最短的时间内执行完成,这就促使人们迫切希望找到一种优化指令执行过程的方案.考虑上述执行过程,我们发现洗茶壶这步完全没有必要等待烧开水完成,也就是说洗茶壶和洗水杯完全可以和烧开水同时进行,这么一来,优化过的流程如图:

 

这里写图片描述

这种通过改变原有执行顺序而减少时间的执行过程我们被称之为乱序执行,也称为重排.到现在为止,我们已经弄明白了什么是按序执行,什么是乱序.那接下来就看看处理器中的乱序执行技术.


乱序执行技术

处理器乱序执行

随着处理器流水线技术和多核技术的发展,目前的高级处理器通过提高内部逻辑元件的利用率来提高运行速度,通常会采用乱序执行技术.这里的乱序和上面谈到烧水煮茶的道理是一样的.

先来看一张处理器的简要结构图:

 

这里写图片描述

处理器从L1 Cache中取出一批指令,分析找出那些不存在相互依赖的指令,同时将其发射到多个逻辑单元执行,比如现在有以下几条指令:

LDR   R1, [R0];
ADD   R2, R1, R1;
ADD   R4,R3,R3;

通过分析发现第二条指令和第一条指令存在依赖关系,但是和第3条指令无关,那么处理器就可能将其发送到两个逻辑单元去执行,因此上述的指令执行流程可能如下:

 

这里写图片描述

可以说乱序执行技术是处理器为提高运算速度而做出违背代码原有顺序的优化.在单核时代,处理器保证做出的优化不会导致执行结果远离预期目标,但在多核环境下却并非如此.

首先多核时代,同时会有多个核执行指令,每个核的指令都可能被乱序;另外,处理器还引入了L1,L2等缓存机制,每个核都有自己的缓存,这就导致逻辑次序上后写入内存的数据未必真的最后写入.最终带来了这么一个问题:如果我们不做任何防护措施,处理器最终得出的结果和我们逻辑得出的结果大不相同.比如我们在一个核上执行数据的写入操作,并在最后写一个标记用来表示之前的数据已经准备好,然后从另一个核上通过判断这个标志来判定所需要的数据已经就绪,这种做法存在风险:标记位先被写入,但是之前的数据操作却并未完成(可能是未计算完成,也可能是数据没有从处理器缓存刷新到主存当中),最终导致另一个核中使用了错误的数据.

编译器指令重排

除了上述由处理器和缓存引起的乱序之外,现代编译器同样提供了乱序优化.之所以出现编译器乱序优化其根本原因在于处理器每次只能分析一小块指令,但编译器却能在很大范围内进行代码分析,从而做出更优的策略,充分利用处理器的乱序执行功能.

乱序的分类

现在来总结下所有可能发生乱序执行的情况:

  • 现代处理器采用指令并行技术,在不存在数据依赖性的前提下,处理器可以改变语句对应的机器指令的执行顺序来提高处理器执行速度
  • 现代处理器采用内部缓存技术,导致数据的变化不能及时反映在主存所带来的乱序.
  • 现代编译器为优化而重新安排语句的执行顺序

小结

尽管我们看到乱序执行初始目的是为了提高效率,但是它看来其好像在这多核时代不尽人意,其中的某些"自作聪明"的优化导致多线程程序产生各种各样的意外.因此有必要存在一种机制来消除乱序执行带来的坏影响,也就是说应该允许程序员显式的告诉处理器对某些地方禁止乱序执行.这种机制就是所谓内存屏障.不同架构的处理器在其指令集中提供了不同的指令来发起内存屏障,对应在编程语言当中就是提供特殊的关键字来调用处理器相关的指令.


内存屏障

处理器乱序规则

上面我们说了处理器会发生指令重排,现在来简单的看看常见处理器允许的重排规则,换言之就是处理器可以对那些指令进行顺序调整:

处理器 Load-Load Load-Store Store-Store Store-Load 数据依赖
x86 N N N Y N
PowerPC Y Y Y Y N
ia64 Y Y Y Y N

表格中的Y表示前后两个操作允许重排,N则表示不允许重排.与这些规则对应是的禁止重排的内存屏障.

注意:处理器和编译都会遵循数据依赖性,不会改变存在数据依赖关系的两个操作的顺序.所谓的数据依赖性就是如果两个操作访问同一个变量,且这两个操作中有一个是写操作,那么久可以称这两个操作存在数据依赖性.举个简单例子:

a=100;//write
b=a;//read

或者
a=100;//write
a=2000;//write
或者
a=b;//read
b=12;//write

以上所示的,两个操作之间不能发生重排,这是处理器和编译所必须遵循的.当然这里指的是发生在单个处理器或单个线程中.

内存屏障的分类

在开始看一下表格之前,务必确保自己了解Store和Load指令的含义.简单来说,Store就是将处理器缓存中的数据刷新到内存中,而Load则是从内存拷贝数据到缓存当中.

屏障类型 指令示例 说明
LoadLoad Barriers Load1;LoadLoad;Load2 该屏障确保Load1数据的装载先于Load2及其后所有装载指令的的操作
StoreStore Barriers Store1;StoreStore;Store2 该屏障确保Store1立刻刷新数据到内存(使其对其他处理器可见)的操作先于Store2及其后所有存储指令的操作
LoadStore Barriers Load1;LoadStore;Store2 确保Load1的数据装载先于Store2及其后所有的存储指令刷新数据到内存的操作
StoreLoad Barriers Store1;StoreLoad;Load1 该屏障确保Store1立刻刷新数据到内存的操作先于Load2及其后所有装载装载指令的操作.它会使该屏障之前的所有内存访问指令(存储指令和访问指令)完成之后,才执行该屏障之后的内存访问指令

StoreLoad Barriers同时具备其他三个屏障的效果,因此也称之为全能屏障,是目前大多数处理器所支持的,但是相对其他屏障,该屏障的开销相对昂贵.在x86架构的处理器的指令集中,lock指令可以触发StoreLoad Barriers.

现在我们综合重排规则和内存屏障类型来说明一下.比如x86架构的处理器中允许处理器对Store-Load操作进行重排,与之对应有StoreLoad Barriers禁止其重排.


as-if-serial语义

无论是处理器还是编译器,不管怎么重排都要保证(单线程)程序的执行结果不能被改变,这就是as-if-serial语义.比如烧水煮茶的最终结果永远是煮茶,而不能变成烧水.为了遵循这种语义,处理器和编译器不能对存在数据依赖性的操作进行重排,因为这种重排会改变操作结果,比如对:

a=100;//write
b=a;//read

重排为:

b=a;
a=100;

此时b的值就是不正确的.如果不存在操作之间不存在数据依赖,那么这些操作就可能被处理器或编译器进行重排,比如:

a=10;
b=200;
result=a*b;

它们之间的依赖关系如图:

 

这里写图片描述

由于a=10b=200之间不存在依赖关系,因此编译器或处理可以这两两个操作进行重排,因此最终执行顺序可能有以下两种情况:

这里写图片描述


但无论哪种执行顺序,最终的结果都是对的.

 

正是因为as-if-serial的存在,我们在编写单线程程序时会觉得好像它就是按代码的顺序执行的,这让我们可以不必关心重排的影响.换句话说,如果你从来没有编写多线程程序的需求,那就不需要关注今天我所说的一切.

江湖人称小白哥,打赏我会生气的,不信你试试?



作者:涅槃1992
链接:https://www.jianshu.com/p/ede6da3164c5
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

谈乱序执行和内存屏障 的相关文章

  • eclipse的Toggle Block Selection Mode功能---列模式

    快捷键 Alf Shift A Toggle Block Selection Mode 块选择模式开关 用法 首先用快捷键或者toolbar打开选择模式 会出现一个十字 表明块选择模式已开启 选中要统一修改的块 可以从 剪贴板ctrl v替
  • 2020大数据技术体系结构图,你值得拥有

    大数据技术体系结构图 JAVA JAVAEE核心 hadoop生态体系及各种框架 spark生态体系 Flink生态体系 JAVA JAVAEE核心 hadoop生态体系及各种框架 spark生态体系 Flink生态体系 希望对大家有所帮助
  • [设计模式] GoF 23种经典设计模式

    原文链接 https www yuque com cppdev patterns zainii GOF设计模式 Gang of Four 四人帮 设计模式的经典书籍 设计模式 可复用面向对象软件的基础 是由四个人共同完成 故GOF设计模式特
  • Spring Boot学习笔记

    文章目录 Spring Boot Spring Boot 整合 JSP Spring Boot HTML Thymeleaf 常用语法 Spring Boot 数据校验 Spring Boot 整合 JDBC Spring Boot 整合
  • Python中创建Dataframe的方法

    介绍4种在Python中创建Dataframe的方法 1 由数组 list组成的字典创建DataFrame import pandas as pd import numpy as np In 1 data pd DataFrame name
  • 【图卷积神经网络】1-入门篇:为什么使用图神经网络(上)

    在本节中 将涵盖以下主要内容 为什么使用图 为什么学习图 为什么使用图神经网络 为什么使用图 首先需要回答的问题是 为什么对图感兴趣 图论是对图进行数学研究的学科 它已经成为理解复杂系统和关系的基本工具 图是一种将节点 也称为顶点 和连接这
  • 03.1-常见端口及攻击汇总

    常用端口号及攻击方向汇总 文件共享服务端口 端口号 端口说明 攻击方向 21 22 69 Ftp Tftp 文件传输协议 允许匿名的上传 下载 爆破和嗅探操作 2049 Nfs 服务 配置不当 139 Samba 爆破 未授权访问 远程代码
  • 利用Qt的pro文件中DEFINES实现条件宏

    想利用宏定义实现不同的代码功能 但不想每次都去代码里注释或者不注释相关的宏定义变量 在这种情况下 就可以在pro里面利用DEFINES字段实现相关宏的定义 例如pro里面加上 DEFINES MYTEST 在代码里就可以使用 ifdef M
  • 服务发现框架选型,Consul还是Zookeeper还是etcd

    背景 本文并不介绍服务发现的基本原理 除了一致性算法之外 其他并没有太多高深的算法 网上的资料很容易让大家明白上面是服务发现 想直接查看结论的同学 请直接跳到文末 目前 市面上有非常多的服务发现工具 Open Source Service
  • LLVM Cookbook

    链接 https blog csdn net qq 23599965 article details 88344459 https github com zy445566 llvm guide zh 万花筒 用LLVM实现语言 备注 万花筒
  • 【Linux】性能优化-linux命令nicstat 网络性能监控

    文章目录 1 概述 1 概述 Unix 里一个受欢迎的命令行工具就是 nicstat 它可以显示每个网络接口的流量概要 包括网络接口的使用度 nicstat 5 Time Int rKB s wKB s rPk s wPk s rAvs w
  • Git_回退到上一次commit与pull

    git 回退到上个版本 rollback 回滚 git reset HEAD git 回退到上一版本
  • Cocos2d-x3.2 Sprite精灵类的创建与设置

    3 0版本以后 sharedXXX方法改成了getInstance方法 Director getInstance gt getVisibleSize 1 2 3 4 5 6 7
  • CSS网页页面图像灰色滤镜写法示例

    阿酷TONY 原创 2022 12 1 长沙 grayscale 函数是一个内置函数 用于对图像应用滤镜以设置图像的灰度 用法 grayscale amount 参数 此函数接受包含灰度值的单个参数量 灰度值根据数量和百分比设置 值0 表示
  • numpy手写NLP模型(四)———— RNN

    numpy手写NLP模型 四 RNN 1 模型介绍 2 模型 2 1 模型的输入 2 2 模型的前向传播 2 3 模型的反向传播 3 模型的代码实现 1 模型介绍 首先介绍一下RNN RNN全程为循环神经网络 主要用来解决一些序列化具有顺序

随机推荐

  • 【OPPO提前批】第二轮简历筛选马上开启!7.28日截止投递

    网易校招 社招 社招网易招聘 网易招聘 网易继续大量岗位在招 想看新机会的速来 base广州 杭州 北京 上海等地 在招 测试 开发 算法 前端 后端 Go Java 华为od审批 华为od审批 只能说od面试机试都是小问题 最厉害的还是审
  • [563]安装Keras(TensorFlow做后端)

    在前文TensorFlow练习1中我提到过一个使用TensorFlow做后端的高级库 这个库叫Keras 它是一个抽象层次比较高的神经网络Python库 在TensorFlow练习1中我是手动定义神经网络 有了这货几行代码就可搞定 最开始K
  • 这些node开源工具你值得拥有(上)

    前言 文章的灵感来源于 社群中某大佬分享一个自己耗时数月维护的github项目 awesome nodejs 或许你跟我一样会有一个疑惑 github上其实已经有个同类型的awesome nodejs库且还高达41k 重新维护一个新的意义何
  • 如何高效的代码审查?这里有四点建议

    代码审查可以帮助你成长 学会接受和提供反馈 扫码关注 Java学研大本营 加入读者群 分享更多精彩 今天让我们讨论一下代码审查 为什么它很重要 以及你应该如何学习成为一个更好的审查者并提供可操作的反馈 为什么代码审查很重要 我在一家初创公司
  • vue实现多选点击、点击谁谁变色,一键重置颜色如何实现?

  • 使用burp suite抓取火狐浏览器的数据包

    老版火狐记得断网安装并取消其自动更新 火狐中转发代理的设置 之后在burp中 proxy intercept intercept is on 就能抓包了 如果需要查看返回包的内容 Action Do intercept Response t
  • EXP-00091: Exporting questionable statistics 问题处理方法

    1 环境 操作系统 HP UX SCUP15 B 11 31 数据库 oracle 10 2 0 5 2 问题说明 在我们对Oracle做EXP的过程中 可能会经常遇到EXP 00091 Exporting questionable sta
  • logstash快速安装插件并打离线包

    一 背景 1 生产环境是不能直接联网的 2 要给 Logstash 安装上 logstash input jdbc 插件用来读取 Mysql 数据到 ES 进行全文检索 二 原生安装 usr share logstash bin logst
  • 预加重、去加重和均衡

    预加重 去加重和均衡 随着信号速率的增加 高速信号的趋肤效应和传输线的介质损耗 使信号在传输 过程中受损很大 为了在接收终端能得到比较好的波形 就需要对受损的信号进 行补偿 常用的补偿技术有 预加重 去加重和均衡在介绍这三种信号补偿技术 之
  • Mysql-错误处理: Found option without preceding group in config file

    1 问题描述 安装MYSQL时 在cmd中 初始化 数据库时 输入命令 mysqld initialize console cmd报错 D mysql 8 0 19 winx64 bin gt mysql initialize consol
  • 如何读取redis中的key值中的结果

    redis的值有5种类型 不同的类型有不同的命令来获取 字符直接 get key 队列 左端弹出一个元素 LPOP key 哈希 HGET key field 集合 SMEMBERS key 返回集合中的所有元素 有序集合ZRANGE ke
  • 【python】mac+appium获取安卓app的appPackage和appActivity

    本文介绍两种亲测最简单有效的办法 方法一 如果你有一台mac本和win的电脑 但是想用mac测安卓app 在win的电脑上解压后缀为 apk的文件 用notepad 等打开解压后文件夹中的AndroidManifest xml即可看到app
  • 内测体验:JetBrains面向未来的Fleet编辑器是什么+究竟怎样 使用初体验+与vsc对比

    异想之旅 本人原创博客完全手敲 绝对非搬运 全网不可能有重复 本人无团队 仅为技术爱好者进行分享 所有内容不牵扯广告 本人所有文章仅在CSDN 掘金和个人博客 一定是异想之旅域名 发布 除此之外全部是盗文 引言 上个月 我在看到某公众号推广
  • Connected Components?【Codeforces 920E】【补图的联通块的个数】

    Educational Codeforces Round 37 Rated for Div 2 E 怎么说呢 跟这道题是一样的 这道题就变得很模板了 原题 include
  • Unity2D input.GetAxis()与input.GetAxisRaw()的相同点、区别以及简单用法

    目录 前言 一 input GetAxis 与input GetAxisRaw 的相同点 区别 二 简单用法 1 input GetAxis 2 input GetAxisRaw 三 总结 前言 input GetAxis 与input G
  • v-model的使用

    目录 一 v model 的原理 二 v model 结合radio 使用 三 v model 结合checkbox 类型单选使用 四 v model 结合checkbox 多选使用 五 v model 结合select 使用 六 v mo
  • 消灭兔子【贪心+堆】

    题目链接 51nod 1191 消灭兔子 兔子这么可爱 怎么能消灭呢 我们可以用贪心的办法来解决这个问题 因为每个箭只能使用一次 所以 我们将兔子血量从高往低排列 先做掉高血量兔子 然后再看低血量兔子 保证了伤害高但是价值小的武器假如在之前
  • 编程实现RSA数字签名

    一 实验目的 理解RSA数字签名 并运用编程实现RSA数字签名 二 实验过程 1 学习RSA算法及RSA数字签名算法流程 2 RSA数字签名原理 当发送方想要给接收方发送数据 并想进行数字签名的时候 发送方只需要利用自己的私钥 对数据进行数
  • python进阶:线程、进程和协程

    前言 线程 进程和协程都是实现多任务 同一时间可以做多个事情就叫做多任务 的方法 并发 一段时间内做不同的事情 比如一个人吃饭喝水 吃的时候不能喝 喝的时候不能吃 只有在一段时间 吃了再喝或者喝了在吃 并行 同一时间做不同的事情 比如边听音
  • 谈乱序执行和内存屏障

    10多年前的程序员对处理器乱序执行和内存屏障应该是很熟悉的 但随着计算机技术突飞猛进的发展 我们离底层原理越来越远 这并不是一件坏事 但在有些情况下了解一些底层原理有助于我们更好的工作 比如现代高级语言多提供了多线程并发技术 如果不深入下来