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

建议阅读文档

乱序的定义


我们先不考虑乱序,先把顺序 定义一下
C程序顺序 				: 可能会被优化,与 二进制程序不一样
二进制(汇编)程序顺序   	: cpuA 读到的顺序
执行顺序 			 	: cpuA 执行的顺序
感知顺序 			 	: cpuB 感知到的 "CPUA执行的顺序" ,"CPUA执行的顺序" 不一定相同

这里的乱序 指的是 编译器乱序,观察者乱序 // (如果是单核呢?没有观察者吗?)
	观察者乱序的种类包括
		1. 执行者乱序(out of order execution) 导致的 观察者乱序
		2. memory order 情景1 : 可以交换执行顺序的内存操作 由于交换 导致的 观察者乱序 // 是 执行结果顺序还是 执行顺序??? TODO
		3. memory order 情景2 : 没有交换执行顺序,但是执行结果顺序由于 cache miss 而交换 导致的观察者乱序

		// 其实可以这么说 "out of order execution" 包括 "memory order 中某些可以交换执行顺序的内存操作的乱序"

屏障的定义

屏障 指的是 编译器屏障,内存屏障(在这里用cpu内存屏障来代替)
	// 其中 cpu内存屏障也是 memory order的补丁

cpu 内存屏障可以解决三个问题
	1. 编译器优化导致的乱序
	2. cpu运行优化导致的乱序
	3. 弱 memory order 导致的问题

有人要说了,那 cpu 内存屏障 可以替代 编译器屏障 ,为什么 还要 编译器屏障呢?
	因为 cpu 内存屏障 的代价 比 编译器屏障 大 ,会增加程序运行时间 


硬件及软件技术的变化

1. 编译器的优化
	当优化开关打开时
	编译器会真对不同的cpu 做一些优化
	并且会优化掉编译器认为重复的操作
	
	例如
		情景1
		我连续写了串口的发送寄存器两次,他会优化掉
		我就是想写两次来打印两个字符
		
2. 运行时的优化
	硬件一设计出来就有,无法关闭
	为了 提高 " 流水线 " 性能
	新式处理器可以采用 " 超标量 体系结构 “ 和 ” 乱序执行 " 技术 , 可以在 一个时钟周期 中 并行执行多条指令 ;
	但是 CPU 执行优化会导致 指令乱序执行 , 后面的指令先于前面的指令执行 , 导致 寄存器中的值冲突 ;
	
	例如
		情景1 : out of order execution 导致的观察者乱序
		我的程序先配置 串口速率寄存器,然后 向 串口发送寄存器 写值
		cpu 发射的时候. 可能串口速率寄存器还没写值 , 但是 串口发送寄存器已经写了一个数了

		情景2 : memory order 导致的观察者乱序
		我的程序先配置 串口速率寄存器,然后 向 串口发送寄存器 写值
		cpu 发射的时候. 可能串口速率寄存器配了值(效果还没出来,硬件还没处理好这个值), 串口发送寄存器马上就写了一个数.
		

优化带来的问题

1. 编译器优化
	优化后,不是我想要的代码逻辑
	不优化,指令太多,跑的时间太长
	
	诉求 : 不想优化的地方你不许优化
2. 运行时优化
	优化后,先后顺序会搞反(包括执行顺序,效果顺序)
	不优化,没有充分利用硬件,跑的不够快 ??? 错了,没得选,必须得优化
	
	诉求 : 某些顺序有要求的指令序列,你得按照顺序跑

阻止被优化的技术

编译器内存屏障
1. 编译器优化(-O2 才能打开,不打开默认是不优化.你写什么顺序,就是什么顺序.你写从内存中load,就不会优化到register)
	用编译器的提示指令来处理 // 编译器内存栅栏(编译器)
		1. 交换指令顺序/合并指令
			1.显式 #define barrier() asm volatile("": : :“memory”)
			2.隐式 调用一个函数// 注意 : 不是 inline 一个函数
			3.显式 asm volatile(“yield” ::: “memory”); // armv7及以上架构中可应用
			4.显式 volatile 
		2. 编译器做寄存器的优化,而不再内存中load
			1.显式 #define barrier() asm volatile("": : :“memory”)
			2.隐式 调用一个任意函数// 注意 : 不是 inline 一个函数
			3.显式 asm volatile(“yield” ::: “memory”); // armv7及以上架构中可应用
			4.显式 READ_ONCE // linux 中可应用,当然,你可以移植到你的代码中
			5.显式 volatile 
编译器内存屏障实验代码
https://gitee.com/suweishuai/baremetal
该仓库 做了 基于四个架构(arm32/arm64/rv32/rv64)的 编译器内存优化 编译器内存屏障 练习
优化等级涉及 7种等级
无Ox 等价于 -O0
-Og
-O  等价于-O1
-Os
-O2
-O3
-Ofast
屏障方法涉及 三种
1. 显式 #define barrier() asm volatile("": : :“memory”)
2. 隐式 调用一个函数// 注意 : 不是 inline 一个函数
3. 显式 volatile
CPU 内存屏障
2. 运行时优化(一直存在)
	用架构定义的提示指令来处理 // 内存栅栏(cpu)
		注意 : 这个指令完全是架构定义的,所以不同的架构 实际调用的指令不同
		注意 : 所有的CPU内存屏障封装都隐式包含了编译器屏障
		可以用内嵌汇编的方式调用,但是linux中定义好了一些 cpu内存屏障宏,为什么不用呢?
			smp_wmb smp_rmb smp_mb	

RISCV的CPU内存屏障宏
#define nop()		__asm__ __volatile__ ("nop")

#define RISCV_FENCE(p, s) \
	__asm__ __volatile__ ("fence " #p "," #s : : : "memory")

/* These barriers need to enforce ordering on both devices or memory. */
#define mb()		RISCV_FENCE(iorw,iorw)
#define rmb()		RISCV_FENCE(ir,ir)
#define wmb()		RISCV_FENCE(ow,ow)

/* These barriers do not need to enforce ordering on devices, just memory. */
#define __smp_mb()	RISCV_FENCE(rw,rw)
#define __smp_rmb()	RISCV_FENCE(r,r)
#define __smp_wmb()	RISCV_FENCE(w,w)

ARM的CPU内存屏障宏
#define isb() __asm__ __volatile__ ("isb" : : : "memory")
#define dsb() __asm__ __volatile__ ("dsb" : : : "memory")
#define dmb() __asm__ __volatile__ ("dmb" : : : "memory")

#define mb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
#define rmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
#define wmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)

各个指令集架构的内存屏障指令

riscv
	fence			sync thread
	fence.i  		sync instr & data
	sfence.vma  	virtual memory fence

armv8
	dmb				data memory barrier
	dsb 			data sync barrier
	isb				instruction sync barrier

armv7
	dmb				data memory barrier
					在DMB之后的显示的内存访问执行前,保证所有在DMB指令之前的内存访问完成。
	dsb 			data sync barrier
					等待所有在DSB指令之前的指令完成(之后再执行后续的指令,译注)。
	isb				instruction sync barrier
					清除(flush)流水线,使得所有ISB之后执行的指令都是从cache或内存中获得的(而不是流水线中的)
armv6
	dmb
	dsb
	PrefetchFlush
		The PrefetchFlush instruction flushes the pipeline in the processor,
		so that all instructions following the pipeline flush are fetched 
		from cache or memory after the instruction has been completed.


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 内存屏障是一种CPU指令,用于保证内存访问的有序性。它可以分为编译屏障和CPU执行乱序屏障。 编译屏障是在代码编译时插入的指令,用于告诉编译器在这个位置需要生成一条内存屏障指令。它可以保证在编译后生成的机器码中,这个位置的内存访问会被正确地序列化。 CPU执行乱序屏障是在CPU级别执行的指令,用于告诉CPU在这个位置需要保证内存访问的顺序性。在现代CPU中,由于硬件优化和多核并发等原因,CPU会对指令进行乱序执行,而乱序屏障可以保证在这个位置之前的所有内存访问都已经完成,从而避免了乱序执行带来的问题。 总的来说,内存屏障是一种非常重要的机制,可以保证多线程程序中的内存访问顺序性,避免出现数据竞争等问题。 ### 回答2: 内存屏障是指用于控制内存访问顺序的指令或者指令序列。根据其功能和作用方式的不同, 内存屏障可以分为编译屏障和CPU执行乱序屏障。 编译屏障是在编译器层面进行优化控制的屏障编译器在进行程序优化时,可能会对代码进行重排,以提高执行效率。然而,有些代码的执行顺序是有严格要求的,此时就需要使用编译屏障来保证指令的顺序。编译屏障可以用于控制指令的插入位置,确保指令的执行顺序符合预期。编译屏障通常是通过特殊的指令或者关键字来实现的,例如在C语言中的"__asm__ __volatile__"关键字。 CPU执行乱序屏障是在CPU层面进行指令乱序执行时的控制屏障。现代处理器在执行指令时会使用乱序执行技术,乱序执行可以提高指令级并行度从而提高处理器的性能。然而,在某些情况下,由于指令之间存在依赖关系,需要保证指令的执行顺序,此时就需要使用乱序屏障乱序屏障可以阻止指令乱序执行的同时也确保了数据的一致性。乱序屏障一般是通过特殊的指令来实现的,例如在x86架构中的"mfence"指令。 总的来说,编译屏障主要是用于控制编译器对代码的优化,保证指令的执行顺序;而CPU执行乱序屏障主要是用于控制CPU对指令的乱序执行,保证指令的执行顺序。两者在不同的层面上起到了优化和控制的作用,都是为了保证程序的正确执行和数据的一致性。 ### 回答3: 内存屏障是一种在并发编程中用来确保内存操作有序性的机制。内存屏障分为编译屏障和CPU执行乱序屏障两种类型。 编译屏障是在编译器层面上插入的指令,用于告诉编译器在指定位置之前的所有内存访问操作必须完成,并且在指定位置之后的所有内存访问操作必须等待。编译屏障可以通过优化和重排指令来提高程序执行效率,但是在多线程环境下可能会导致并发访问数据的顺序问题。因此,通过插入编译屏障来限制指令重排,确保内存操作按照预期的顺序进行。 CPU执行乱序屏障是在指令执行层面上插入的机制,用于告诉CPU在指定位置之前的所有内存访问操作必须完成,并且在指定位置之后的所有内存访问操作必须等待。CPU执行乱序屏障主要解决CPU乱序执行指令的问题,确保内存操作的顺序性。在现代处理器中,由于乱序执行可以提高指令执行效率,但可能导致结果和预期不符。因此,通过插入CPU执行乱序屏障来确保内存操作的有序性。 总结起来,编译屏障和CPU执行乱序屏障是为了解决并发编程中的内存操作顺序问题而设计的机制。编译屏障编译器层面上限制指令重排,确保内存操作有序进行;CPU执行乱序屏障在指令执行层面上限制指令乱序执行,保证内存操作的有序性。这两种屏障在不同的层面上发挥作用,共同保证程序的正确执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值