java八股文面试[多线程]——指令重排序

关于a的操作,由原来的6个指令,变成了4个指令。

1. 指令重排序的介绍
1)指令重排序的类型
在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。

重排序分三种类型:
编译器优化的重排序 编译器在不改变单线程程序语义的前提下(代码中不包含synchronized关键字),可以重新安排语句的执行顺序。
指令级并行的重排序 现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
内存系统的重排序 由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行


重排序又可以分为两大类

一是编译器重排序:编译器优化重排序,

二是处理器重排序:指令级并行重排序、内存系统重排序。


2)重排序的流程
从java源代码到最终实际执行的指令序列,会分别经历下面三种重排序:

在这里插入图片描述


3)重排序意义
处理器为提高运算速度而做出违背代码原有顺序的优化


4)存在的问题
这三种重排序方法都可能会导致多线程程序出现内存可见性问题

2. 内存系统重排序
1)内存系统的读操作
为提高从内存读取的效率,对读操作进行优化,将其称为程序乱序执行优化。CPU为了提高指令执行效率,会在一条指令执行过程中(比如去内存读数据(慢100倍)),去同时执行另一条指令,前提是,两条指令必须是独立的、没有依赖关系,cpu一般是依赖指令间的内存引用关系来判断的指令间的”独立关系”。


2)内存系统的写操作
写操作的优化称为合并写入技术。现代的处理器使用写缓冲区来临时保存向内存写入的数据。
写操作优化的过程:乱序当cpu执行存储指令时,它会首先试图将数据写到离cpu最近的L1_cache, 如果此时cpu出现L1未命中,则会访问下一级缓存。速度上L1_cache基本能和cpu持平,其他的均明显低于cpu,L2_cache的速度大约比cpu慢20-30倍,而且还存在L2_cache不命中的情况,又需要更多的周期去主存读取。其实在L1_cache未命中以后,cpu就会使用一个另外的缓冲区,叫做合并写存储缓冲区。这一技术称为合并写入技术。


在请求L2_cache缓存行的所有权尚未完成时,cpu会把待写入的数据写入到合并写存储缓冲区,该缓冲区大小和一个cache line大小,一般都是64字节。这个缓冲区允许cpu在写入或者读取该缓冲区数据的同时继续执行其他指令,这就缓解了cpu写数据时cache miss时的性能影响。当后续的写操作需要修改相同的缓存行时,在将后续的写操作提交到L2缓存之前,可以进行缓冲区写合并。

这些64字节的缓冲区维护了一个64位的字段,每更新一个字节就会设置对应的位,来表示将缓冲区交换到外部缓存时哪些数据是有效的。当然,如果程序读取已被写入到该缓冲区的某些数据,那么在读取缓存数据之前会先去读取本缓冲区的。经过上述步骤后,缓冲区的数据还是会在某个延时的时刻更新到外部的缓存(L2_cache).如果能在缓冲区传输到缓存之前将其尽可能填满,这样的效果就会提高各级传输总线的效率,以提高程序性能。


3)写缓冲区造成的优势
优势1,合并写缓冲区中对同一内存地址的多次写操作,可以减少对内存总线的占用;

优势2,通过以批处理的方式刷新写缓冲区;

优势3,写缓冲区可以保证指令流水线持续运行,避免由于处理器停顿下来等待向内存写入数据而产生的延迟。


4)出现问题的原因
单核时代处理器做出的优化可以保证执行结果不会远离预期目标,但是,在多核时代却并非如此。在多核时代,同时会有多个核同时执行指令,每一个核的指令都可能被乱序。另外,处理器还引入了L1,L2,…,Ln等多级缓存机制,每个核心都有自己的缓存机制,这样就导致了逻辑次序上后写入内存的数据未必真的最后写入。最后就带来一个问题,如果不做任何防护措施,处理器最终得出的结果和逻辑得出结果会大不相同。
比如,在一个核上执行写入操作,并在最后写一个标记用来表示操作完毕,之后从另外一个核上通过判断这个标记来判定所需要的数据是否已经就绪,这种做法就存在一定风险:标记位先被写入但之前的操作却并未完成(可能是未计算完成,也可能是数据没有从处理器缓存刷新到主存中,最终导致另外的核使用了错误的数据)。

3. 内存屏障
3.1. 硬件内存屏障
1)CPU内存屏障(硬件内存屏障)
功能:为了解决代码的乱序执行问题,在CPU级别上引入了内存屏障,这里跟JAVA的内存屏障不是一个问题
分类:
指令 功能
sfence 在sfence指令前的写操作必须在sfence指令后的写操作之前完成
lfence 在lfence指令前的读操作必须在lfence指令后的读操作之前完成
mfence 在mfence指令前的读写操作必须在mfence指令后的读写操作之前完成
原子指令:例如X86中的lock指令,执行时会锁住内存子系统来保障执行顺序不改变,甚至能跨多个CPU。
Software Locks通常使用内存屏障原子指令来实现变量的可见性和执行顺序不变

3.2. 内存系统的内存屏障
1)使用背景
对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。


2)基本使用
内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。

JMM把内存屏障指令分为下列四类:
LoadLoad屏障 语句:Load1; LoadLoad; Load2
在Load2及后续读取操作要在读取的数据被访问前,保证Load1要读取的数据被读取完毕
StoreStore屏障 语句:Store1; StoreStore; Store2
在Store2及后续写入操作执行前,保证Store1的写入操作对其他处理器可见
LoadStore屏障 语句:Load1; LoadStore; Store2
在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕
StoreLoad屏障 语句:Store1; StoreLoad; Load2
在Load2及后续所有读取操作执行前,保证Store1的写入对其他处理器是可见

3)StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。现代的多处理器大都支持该屏障(其他类型的屏障不一定被所有处理器支持)。执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(buffer fully flush)


4)允许指令重排序的列表
下面是常见处理器允许的重排序类型的列表:

在这里插入图片描述
注:上表单元格中的“N”表示处理器不允许两个操作重排序,“Y”表示允许重排序。
从上表我们可以看出:常见的处理器都允许Store-Load重排序;常见的处理器都不允许对存在数据依赖的操作做重排序。sparc-TSO和x86拥有相对较强的处理器内存模型,它们仅允许对写-读操作做重排序(因为它们都使用了写缓冲区)。

4. happens-before关系
1)基本介绍
JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间


2)happens-before规则
与程序员密切相关的happens-before规则如下:
程序顺序规则 一个线程中的每个操作,happens- before 于该线程中的任意后续操作
监视器锁规则 对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁
volatile变量规则 对一个volatile域的写,happens- before 于任意后续对这个volatile域的读
传递性 如果A happens- before B,且B happens- before C,那么A happens- before C
Start规则
Join规则
特殊说明:两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。


3)happens-before与JMM的关系

在这里插入图片描述
如上图所示,一个happens-before规则通常对应于多个编译器处理器重排序规则。对于java程序员来说,happens-before规则简单易懂,避免java程序员为了理解JMM提供的内存可见性保证而去学习复杂的重排序规则以及这些规则的具体实现。

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

在这里插入图片描述
上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变
注:这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。


2)as-if-serial语义
不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变,编译器,runtime 和处理器都必须遵守as-if-serial语义。即编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。
为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。
as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器,runtime 和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。as-if-serial语义使单线程程序员无需担心重排序会干扰他们,也无需担心内存可见性问题。

 

 参考:

【23版面试突击】你知道什么是指令重排序?为什么要重排序?_哔哩哔哩_bilibili

指令重排序_Waiting_Mr_Liu的博客-CSDN博客

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

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

相关文章

YOLO V5 和 YOLO V8 对比学习

参考文章: 1、YOLOv5 深度剖析 2、如何看待YOLOv8,YOLOv5作者开源新作,它来了!? 3、anchor的简单理解 完整网络结构 YOLO v5和YOLO v8的Head部分 YOLO v8的Head 部分相比 YOLOv5 改动较大,换成了目前主流的解耦头结构…

【springboot】Spring Cache缓存:

文章目录 一、导入Maven依赖&#xff1a;二、实现思路&#xff1a;三、代码开发&#xff1a; 一、导入Maven依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId><…

WordPress主题Zing V2.2.1/模块化WordPress响应式通用企业商城主题

WordPress主题Zing V2.2.1&#xff0c;模块化WordPress响应式通用企业商城主题。 功能介绍 百度熊掌号文章实时推送、原创保护 多设备支持自适应布局&#xff0c;支持电脑、Pad、手机以及各种浏览器 SEO优化首页、文章、页面、分类均支持自定义标题、关键字和描述 速度优化…

Django(7)-项目实战-发布会管理

登录功能 模板页面 sign/templates/index.html <!DOCTYPE html> <html> <head><title>Login Page</title> </head> <body><h1>发布会管理</h1><form action"/login/" method"post"><la…

idea 常用插件和常用快捷键 - 记录

idea 常用插件 记得下载插件完成后&#xff0c;点击 Apply 和 OK Alibaba Java Coding Guidelines 作用&#xff1a;使用该插件可以&#xff0c;自动提示相关的语法格式问题&#xff0c;格式参考 阿里巴巴代码规范 详情链接&#xff1a; 代码规范之Alibaba Java Coding G…

EXCEL中点击单元格,所在行和列都改变颜色

在日常工作中&#xff0c;尤其是办公室工作人群&#xff0c;尝尝需要处理大量的数据&#xff0c;在对数据进行修改时&#xff0c;时长发生看错行的事情&#xff0c;导致数据越改越乱&#xff0c;因此&#xff0c;我常用的一种方法就是选中单元格时&#xff0c;所在行、列标记为…

【AI】数学基础——高数(函数微分部分)

参考&#xff1a;https://www.bilibili.com/video/BV1mM411r7ko?p1&vd_source260d5bbbf395fd4a9b3e978c7abde437 唐宇迪&#xff1a;机器学习数学基础 文章目录 1.1 函数1.1.1 函数分类1.1.2 常见函数指/对数函数分段函数原函数&反函数sigmod函数Relu函数(非负函数)复…

keil5 快捷下载STM32系列芯片器件包的方法

以STM32H7系列的器件包为例,官网的下载网址为 https://sadevicepacksprodus.blob.core.windows.net/pack/Keil.STM32H7xx_DFP.3.1.1.pack 其中STM32H7xx为芯片系列编号,3.1.1为器件包的版本 如需下载其他系列和版本的器件包,只需把网址中的编号和版本换成对应的即可(前提是输入…

K8S:K8S自动化运维容器Docker集群

文章目录 一.k8s概述1.k8s是什么2.为什么要用K8S3.作用及功能4.k8s容器集群管理系统 二.K8S的特性1.弹性伸缩2.自我修复3.服务发现和复制均衡4.自动发布和回滚5.集中化配置管理和秘钥管理6.存储编排7.任务批量处理运行 三.K8S的集群架构四.K8S的核心组件1.Master组件&#xff0…

ATA-1222A宽带放大器的电子实验案例(案例合集)

ATA-1222A宽带放大器是安泰电子打造的高带宽功放产品&#xff0c;其采用ClassAB的工作模式&#xff0c;带宽高达22MHz&#xff0c;饱和输出功率40W&#xff0c;能兼容全球不同地区的电源标准要求。凭借其优异的指标参数受到不少电子工程师的喜欢&#xff0c;其在电子实验中的应…

香港服务器快还是台湾服务器快?

​  基于机房位置不同&#xff0c;香港服务器相对于台湾服务器在访问速度方面有一定的优势。香港服务器拥有CN2线路&#xff0c;因此访问速度较快。在网络服务商方面&#xff0c;中华电信等台湾服务商提供的带宽也具有很高的性价比。 香港服务器对大陆用户的影响 对于大陆用户…

WordPress导航主题/酷啦鱼导航主题模板

酷啦鱼导航主题模板&#xff0c;是一款基于WordPress的导航主题&#xff0c;酷啦鱼导航主题是个人基于wordpresscodestar work框架设计的简洁导航主题。 下载地址&#xff1a;https://bbs.csdn.net/topics/616084697

无涯教程-Android Mock Test函数

本节介绍了与 Android 相关的各种模拟测试。您可以在本地计算机上下载这些样本模拟测试,并在方便时离线解决。每个模拟测试均随附一个模拟测试键,可让您验证最终分数并为自己评分。 Mock Test I Mock Test II Mock Test III Mock Test IV Q 1 -什么是Android&#xff1f; A -A…

Python Opencv实践 - Sobel边缘检测

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_GRAYSCALE) print(img.shape)#Sobel边缘检测 #cv.sobel( src, ddepth, dx, dy[,ksize[, scale[, delta[, borderType]]]] ) #src:…

Flink+Paimon多流拼接性能优化实战

目录 &#xff08;零&#xff09;本文简介 &#xff08;一&#xff09;背景 &#xff08;二&#xff09;探索梳理过程 &#xff08;三&#xff09;源码改造 &#xff08;四&#xff09;修改效果 1、JOB状态 2、Level5的dataFile总大小 3、数据延迟 &#xff08;五&…

Java“牵手”京东商品详情数据,京东API接口申请指南

京东平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取京东商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xff0c;通过…

一种借助MYSQL递归CTE生成所有的组合情况的实现方法

需求说明 有如下表和数据&#xff1a; Nname1户口2查询机构数过多3危险驾驶4多头用信 需要输出name里的所有组合情况&#xff0c;即单个值&#xff0c;两两组合&#xff0c;三个组合、四个组合。结果为2的n次方-1中情况&#xff0c;这里是15。 预期结果为&#xff1a; Com…

Spring 如何解决循环依赖问题 - 三级缓存

1. 什么是循环依赖问题 ? 循环依赖问题是指对象与对象之间存在相互依赖关系&#xff0c;而且形成了一个闭环&#xff0c;导致两个或多个对象都无法准确的完成对象的创建和初始化。 两个对象间的循环依赖&#xff1a; 多个对象间的循环依赖 &#xff1a; 解决 Spring 中的循环…

uniapp 微信小程序仿抖音评论区功能,支持展开收起

最近需要写一个评论区功能&#xff0c;所以打算仿照抖音做一个评论功能&#xff0c;支持展开和收起&#xff0c; 首先我们需要对功能做一个拆解&#xff0c;评论区功能&#xff0c;两个模块&#xff0c;一个是发表评论模块&#xff0c;一个是评论展示区。接下来对这两个模块进行…

【数据结构】C语言队列(详解)

前言: &#x1f4a5;&#x1f388;个人主页:​​​​​​Dream_Chaser&#xff5e; &#x1f388;&#x1f4a5; ✨✨专栏:http://t.csdn.cn/oXkBa ⛳⛳本篇内容:c语言数据结构--C语言实现队列 目录 一.队列概念及结构 1.1队列的概念 1.2队列的结构 二.队列的实现 2.1头文…