为了账号安全,请及时绑定邮箱和手机立即绑定

加载和存储是唯一被重新排序的指令吗?

/ 猿问

加载和存储是唯一被重新排序的指令吗?

炎炎设计 2019-07-12 15:39:54

加载和存储是唯一被重新排序的指令吗?

我读过许多关于内存排序的文章,它们都只说CPU重新排序加载和存储。

CPU(我对x86 CPU特别感兴趣)是否只重新排序加载和存储,而不重新排序它所拥有的其余指令?


查看完整描述

3 回答

?
BIG阳

无序执行保留了按程序顺序运行的错觉。用于单个线程/核心..这类似于C/C+的优化规则:只要可见效果相同,就在内部做您想做的任何事情。

单独的线程只能通过内存进行通信,因此内存操作的全局顺序(加载/存储)是执行的唯一外部可见的副作用。1.

即使是按顺序排列的CPU也可以使它们的内存操作在全球范围内无序可见。(例如,即使是带有存储缓冲区的简单RISC管道也会进行StoreLoad重新排序,比如x86)。启动加载/按顺序存储,但允许它们完成无序(以隐藏缓存错过延迟)的CPU也可以重新排序加载,如果它不明确地避免它(或者像现代x86,积极执行无序,但假装它没有通过仔细跟踪内存排序)。


一个简单的例子:两个ALU依赖链可以重叠

(有关:http:/blog.stuffedco.net/2013/05/测量-抢劫-容量/有关查找指令级并行性的窗口有多大的更多信息,例如,如果将此值提高到times 200你只会看到有限的重叠。还涉及:我写的这个初级到中级的答案关于像Haswell或Skylake这样的OOO CPU是如何发现和利用ILP的。

的影响进行更深入的分析。lfence这里,看了解lfence对具有两个长依赖链的循环的影响,以增加长度。

global _start
_start:
    mov  ecx, 10000000
.loop:
    times 25 imul eax,eax   ; expands to imul eax,eax  / imul eax,eax / ...
 ;   lfence
    times 25 imul edx,edx
 ;   lfence
    dec  ecx
    jnz  .loop

    xor  edi,edi
    mov  eax,231
    syscall          ; sys_exit_group(0)

建(用)nasm + ld)进入x86-64 linux上的静态可执行文件中,这将在预期的750米时钟周期内运行(在skylake上)。25 * 10MIMUL指令乘以3个周期延迟。

注释掉其中一个imul链不会改变运行所需的时间:仍然有7.5亿个周期。

否则,这就是无序执行将两个依赖链交织在一起的明确证据。(imul吞吐量为1/时钟,延迟3个时钟。http://agner.org/optimize/..因此,第三个依赖链可以混在一起,而不会出现太大放缓)。

实际数字taskset -c 3 ocperf.py stat --no-big-num -etask-clock,context-switches,cpu-migrations,page-faults,cycles:u,branches:u,instructions:u,uops_issued.any:u,uops_executed.thread:u,uops_retired.retire_slots:u  -r3 ./imul:

  • 用这两条铁链:

    750566384 +- 0.1%

  • 只有EAX链:

    750704275 +- 0.0%

  • 有一个

    times 50 imul eax,eax

    链:

    1501010762 +- 0.0%

    (速度几乎是预期的两倍)。
  • 带着

    lfence

    防止25块之间的重叠

    imul1688869394 +- 0.0%

    比慢两倍还要糟。

    uops_issued_any

    uops_retired_retire_slots

    两者均为6300万,高于5100万,而

    uops_executed_thread

    仍然是5100万(

    lfence

    不使用任何执行端口,但显然有两个

    lfence

    指令花费6个融合域uop每个.Agner雾只测得2.)

(lfence序列化指令执行而不是内存存储)。如果您没有使用WC内存中的NT加载(这不会发生意外),那么除了停止以后的指令执行,直到以前的指令“在本地完成”之外,这是一种非操作。也就是说,直到他们退休从无序的核心。这可能就是为什么它比总时间多了一倍的原因:它必须等待最后一次。imul在一个街区内要经历更多的管道阶段。)

lfence英特尔总是这样,但是在AMD上,它只是部分地串行化,并启用了谱缓解功能。.


脚注1当两个逻辑线程共享一个物理线程(超线程或其他SMT)时,也有计时侧通道。例如,执行一系列独立的imul如果其他超线程不需要端口1,那么在最近的Intel CPU上,指令将以每时钟1的速度运行。因此,您可以通过在一次逻辑核上设定一个ALU绑定的循环来测量0端口压力的大小。

其他微体系结构侧通道,如高速缓存访问,则更可靠。例如,使用缓存读取侧通道(而不是ALU)最容易利用幽灵/崩溃。

但是与架构支持的读写共享内存相比,所有这些侧通道都很挑剔且不可靠,因此它们只与安全性相关。它们不是在同一程序中故意用于线程间通信的。


Skylake上的MFENCE是一个类似LFENCE的OOOOEEC屏障

mfence关于Skylake出乎意料地阻止了imul,就像lfence尽管没有文献证明它会产生这种效果。(有关更多信息,请参见“移动到聊天”的讨论)。

xchg [rdi], ebx(隐式)lock)根本不会阻止ALU指令的无序执行.更换时,总时间仍为750米周期。lfence带着xchg或者是lock以上考试中的教育教学。

但与mfence,成本增加到1500米周期+2周的时间。mfence指示。为了进行一个受控的实验,我将指令数保持不变,但是移动了mfence指令相邻,所以imul链子可以彼此重新订购,时间缩短到750米+2的时间。mfence指示。

这种skylake行为很可能是要修复的微码更新的结果。玉竹SKL 079来自WC内存的MOVNTDQA可以通过早期的MFENCE指令..该错误指示的存在表明,以前可以执行以后的指令。mfence完成了,所以他们可能做了一个蛮力的修正lfence的微码mfence.

这是另一个赞成使用xchg用于seq-cst商店,甚至lock add作为一个独立的屏障。Linux已经做了这两件事,但是编译器仍然使用mfence因为障碍。看见为什么具有顺序一致性的std:原子存储使用XCHG?

(另请参阅对Linux的障碍选择的讨论这个google组线程,有指向三个单独建议的链接,以供使用。lock addl $0, -4(%esp/rsp)而不是mfence作为一个独立的屏障。


查看完整回答
反对 回复 2019-07-12
?
哔哔one

在可能的、可行的、有利于性能的情况下,无序处理器通常可以重新排序所有指令。由于注册重命名,这对机器代码是透明的,除非在加载和存储的情况下。这就是为什么人们通常只谈论负载和存储的重新排序,因为这是唯一可以观察到的重新排序。


通常,FPU异常也是可以观察到重新排序的东西。大多数无序处理器不精确例外因为这个原因,但不是x86。在x86上,处理器确保报告异常,就好像浮点操作没有重新排序一样。


查看完整回答
反对 回复 2019-07-12
?
烧仙草VB

大多数OOO CPU一般都有精确的异常!否则,页面错误将无法在正确的位置恢复。也许你的意思是说大多数Ooo架构都不精确。FP例外?(有趣的是,我不知道这一点,但很有意义,因为许多微体系结构将FP指令与整数核分开调度。例如,PowerPC甚至会对整型负载重新加载最近的FP存储进行惩罚。) 

查看完整回答
反对 回复 2019-07-12

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信