原文:https://zhuanlan.zhihu.com/p/45808885

背景

在极客星球的群里面讨论

极客星球

很多现代高级语言多提供了多线程并发技术,今天服务器CPU基本上都是多核架构,在Java中,JVM能够根据处理器特性(CPU多级缓存系统多核处理器等)适当对机器指令进行重排序,最大限度发挥机器性能。Java中的指令重排有两次,第一次发生在将字节码编译成机器码的阶段,第二次发生在CPU执行的时候,也会适当对指令进行重排。

写这篇文章的目的,正如题目所说,是想明确下cpu指令乱序这件事。只要是熟悉计算机底层系统的同学就会知道,程序里面的每行代码的执行顺序,有可能会被编译器和cpu根据某种策略,给打乱掉,目的是为了性能的提升,让指令的执行能够尽可能的并行起来。知道指令的乱序策略很重要,原因是这样我们就能够通过barrier(内存屏障)等指令,在正确的位置告诉cpu或者是编译器,这里我可以接受乱序,那里我不能接受乱序等等。从而,能够在保证代码正确性的前提下,最大限度地发挥机器的性能。

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

前言

这里我不打算讨论编译器的乱序策略,后续有机会会写专门的文章来讨论,否则就太乱了。而且,这里讨论的指令乱序,含义稍广些,包括在多核上分别执行的指令间,在时间维度上的乱序。如果在多核cpu层面考虑乱序执行的话,我们要来梳理清楚以下几个概念:单核多核乱序执行顺序提交store bufferinvalid queue。最后会对x86arm/power架构的异同,做一个总结。

单核 vs 多核

从多核的视角上来说,是存在着乱序的可能的。比如,假设存在变量x = 0,cpu0上执行写入W0(x, 1),对x写入1。接着在cpu1上,执行读取R1(x, 0),得到x = 0,这在x86和arm/power的cpu上都是可能出现的。原因是x86上cpu核和cache以及内存之间,存在着store buffer,当W0(x, 1)执行成功后,修改只存在于store buffer中,并未写到cache以及内存上,因此cpu1读取不到最新的x值。对于arm/power来说,同样也有store buffer,而且还可能会有invalid queue,导致cpu1读不到最新的x值。

对于没有invalid queue的x86系列cpu来说,当修改从store buffer刷入cache时,就能够保证在其他核上能够读到最新的修改。但是,对于存在invalid queue的cpu来说,则不一定。

为了能够保证多核之间的修改的可见性,我们在写程序的时候需要加上内存屏障,例如x86上的mfence指令。

乱序执行 vs 顺序提交

我们知道,在cpu中为了能够让指令的执行尽可能地并行起来,从而发明了流水线技术。但是如果两条指令的前后存在依赖关系,比如数据依赖,控制依赖等,此时后一条语句就必需等到前一条指令完成后,才能开始。

cpu为了提高流水线的运行效率,会做出比如:

1)对无依赖的前后指令做适当的乱序和调度;

2)对控制依赖的指令做分支预测;

3)对读取内存等的耗时操作,做提前预读;

等等。以上总总,都会导致指令乱序的可能。

但是对于x86的cpu来说,在单核视角上,其实它做出了Sequential consistency[1]的一致性保障。Sequential consistency的在wiki上的定义如下:

"... the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program."

也就是说,要满足Sequential consistency,必需保障每个处理器的指令执行顺序必需和程序给出的顺序一致。奇怪吧?这不就和我刚才说的指令乱序优化矛盾了嘛?其实并不矛盾,指令在cpu核内部确实是乱序执行和调度的,但是它们对外表现却是顺序提交的。如果把ISA寄存器(如EAX,EBX等)和store buffer,作为cpu对外的接口的话,cpu只需要把内部真实的物理寄存器按照指令的执行顺序,顺序映射到ISA寄存器上,也就是cpu只要将结果顺序地提交到ISA寄存器,就可以保证Sequential consistency。

当然,以上是对x86架构的cpu来说的,ARM/Power架构的cpu在单核上的一致性保证要弱一些,无需保证Sequential consistency,因此也不需要顺序提交,只需保证控制依赖,数据依赖,地址依赖等指令的顺序即可。要想在这些弱一致性模型cpu下保证无关指令间的提交顺序,需要使用barrier指令。

Store Buffer & Invalid Queue

store buffer存在于cpu核与cache之间,对于x86架构来说,store buffer是FIFO,因此不会存在乱序,写入顺序就是刷入cache的顺序。但是对于ARM/Power架构来说,store buffer并未保证FIFO,因此先写入store buffer的数据,是有可能比后写入store buffer的数据晚刷入cache的。从这点上来说,store buffer的存在会让ARM/Power架构出现乱序的可能。store barrier存在的意义就是将store buffer中的数据,刷入cache。

在某些cpu中,存在invalid queue。invalid queue用于缓存cache line的失效消息,也就是说,当cpu0写入W0(x, 1),并从store buffer将修改刷入cache,此时cpu1读取R1(x, 0)仍是允许的。因为使cache line失效的消息被缓冲在了invalid queue中,还未被应用到cache line上。这也是一种会使得指令乱序的可能。load barrier存在的意义就是将invalid queue缓冲刷新。

X86 vs ARM/Power

对于x86架构的cpu来说,在单核上来看,其保证了Sequential consistency,因此对于开发者,我们可以完全不用担心单核上的乱序优化会给我们的程序带来正确性问题。在多核上来看,其保证了x86-tso模型,使用mfence就可以将store buffer中的数据,写入到cache中。而且,由于x86架构下,store buffer是FIFO的和不存在invalid queue,mfence能够保证多核间的数据可见性,以及顺序性。[2]

对于arm和power架构的cpu来说,编程就变得危险多了。除了存在数据依赖,控制依赖以及地址依赖等的前后指令不能被乱序之外,其余指令间都有可能存在乱序。而且,它们的store buffer并不是FIFO的,而且还可能存在invalid queue,这些也同样让并发编程变得困难重重。因此需要引入不同类型的barrier来完成不同的需求。[3]

总结

从上面的介绍可以知道,开发者想要做好并发编程是多么困难的事情,但是我们至少跨出了第一步,也就是定义困难本身。

Reference

[1] Lamport, Leslie. "How to make a multiprocessor computer that correctly executes multiprocess progranm." IEEE transactions on computers 9 (1979): 690-691.

[2] Sewell, Peter, et al. "x86-TSO: a rigorous and usable programmer's model for x86 multiprocessors." Communications of the ACM 53.7 (2010): 89-97.

[3] Maranget, Luc, Susmit Sarkar, and Peter Sewell. "A tutorial introduction to the ARM and POWER relaxed memory models." Draft available from http://www. cl. cam. ac. uk/~ pes20/ppc-supplemental/test7. pdf (2012).

- END -


看完一键三连在看转发点赞

是对文章最大的赞赏,极客重生感谢你

推荐阅读

定个目标|建立自己的技术知识体系

大厂后台开发基本功修炼路线和经典资料

简历的本质

你好,这里是极客重生,我是阿荣,大家都叫我荣哥,从华为->外企->到互联网大厂,目前是大厂资深工程师,多次获得五星员工,多年职场经验,技术扎实,专业后端开发和后台架构设计,热爱底层技术,丰富的实战经验,分享技术的本质原理,希望帮助更多人蜕变重生,拿BAT大厂offer,培养高级工程师能力,成为技术专家,实现高薪梦想,期待你的关注!点击蓝字查看我的成长之路

校招/社招/简历/面试技巧/大厂技术栈分析/后端开发进阶/优秀开源项目/直播分享/技术视野/实战高手等, 极客星球希望成为最有技术价值星球,尽最大努力为星球的同学提供技术和成长帮助!详情查看->极客星球

求点赞,在看,分享三连

当我们在谈论cpu指令乱序的时候,究竟在谈论什么?相关推荐

  1. volatile关键字及编译器指令乱序总结

    本文简单介绍volatile关键字的使用,进而引出编译期间内存乱序的问题,并介绍了有效防止编译器内存乱序所带来的问题的解决方法,文中简单提了下CPU指令乱序的现象,但并没有深入讨论. 以下是我搭建的博 ...

  2. 多线程安全小结-可见性(内存屏障,共享变量副本)、原子性、有序性(编译器优化、cpu流水线乱序)

    摘要: java多线程安全要满足:原子性.可见性.有序性. 手段有:不共享:不可变状态对象:互斥锁:同步工具类:线程安全工具类 本文为本人阅读<Java并发编程实战>的理解和总结,java ...

  3. 无锁数据结构二-乱序控制(栅栏)

    内存栅栏 由于优化会导致对代码的乱序执行,在并发执行时可能带来问题.因此为了并行代码的正确执行,我们需提示处理器对代码优化做一些限制.而这些提示就是内存栅栏(memory barriers),用来对内 ...

  4. CPU乱序发射与内存屏障

    CPU乱序发射与内存屏障 在提出问题之前,先看一段简单的代码. #include <stdio.h> #include <pthread.h> int x = 0, y = 0 ...

  5. riscv cpu硬件访问device区域微乱序的影响

    以下代码,是riscv plic处理一个外部中断的handler程序.在handler程序中 首先写irq_generator组件的clear寄存器,将中断源给清除掉 然后写plic的complete ...

  6. 编译乱序(Compiler Reordering)

    作者:smcdef 发布于:2019-1-23 22:59 分类:内核同步机制 编译器(compiler)的工作就是优化我们的代码以提高性能.这包括在不改变程序行为的情况下重新排列指令.因为compi ...

  7. 简单理解计算机内存乱序

    作者 | 后端技术小牛说   责编 | 张文 头图 | CSDN 下载自视觉中国 本文探讨了自己对内存一致性模型的理解,由于不可避免的需要和操作系统底层打交道,本文主要例子和代码是 C++ 和汇编语言 ...

  8. 乱序和屏障1 : 总览 及 编译器内存屏障

    文章目录 建议阅读文档 乱序的定义 屏障的定义 硬件及软件技术的变化 优化带来的问题 阻止被优化的技术 编译器内存屏障 编译器内存屏障实验代码 CPU 内存屏障 RISCV的CPU内存屏障宏 ARM的 ...

  9. 乱序处理器中的LSQ简介

    乱序处理器中访存指令的操作我一直看的不太懂,其中lsq在流水线中是怎么使用的我也不太清楚,今天看了篇论文,讲的是AMD的经典处理器K7,记录一下: LSU部件可以理解为存储器子系统的最高层,在该部件中 ...

最新文章

  1. java mysql 自动提交_Mybatis的JDBC提交设置/关闭mysql自动提交------关于mysql自动提交引发的惨剧...
  2. poj 1961 Period(KMP)
  3. java 回调(callback)函数简介.
  4. vbs 等于_西门子触摸屏VBS编程quot;陷阱quot;之VBS不支持多线程
  5. C#中获取指定目录下所有目录的名称、全路径和创建日期
  6. 3.1.1 什么是内存?进程的基本原理,深入指令理解其过程
  7. 深度学习(二十)——Ultra Deep Network, 图像超分辨率算法
  8. 转,Oracle中关于处理小数点位数的几个函数,取小数位数,Oracle查询函数
  9. 程序员每天少吃===活120岁
  10. 温故而知新_C语言_define_宏
  11. 【转】Windows IIS注册asp 此操作系统版本不支持此选项 错误解决方法
  12. android统计测试题,Allocation Tracker(Android Studio)
  13. 【树莓派】树莓派常用的一些源
  14. spawn xelatex ENOENT的问题
  15. mpeg1视频解码标准简介
  16. 三、共阳数码管的静态显示
  17. SharePoint 2013 自定义扩展菜单
  18. HR不得不知的Excel技能——模板篇
  19. Java图片转换为PDF并合成同一PDF
  20. css中审核图标,一个简单实用的css loading图标

热门文章

  1. 华为云服务器芯片,云服务器芯片
  2. 一次性掌握JDK、JRE、JVM的概念以及三者之间的关系【2021整理】
  3. 上线清单 —— 20 个 Laravel 应用性能优化项
  4. 【51NOD】1486 大大走格子
  5. 马云致投资者公开信:大数据云计算是阿里未来十年核心战略之一
  6. 11. Container With Most Water
  7. CCNP学习笔记15-RSTP
  8. VMware View 5.0从菜鸟到高手系列 3 -安装View Composer组件篇
  9. 洛谷 - P1989 无向图三元环计数(思维建图)
  10. PAT (Basic Level) 1035 插入与归并(模拟)