为什么需要对 μop 进行分层?

2023-12-21

In “Intel CPU 中的 MicroFusion。”作者:丹尼斯·巴赫瓦洛夫 https://easyperf.net/blog/2018/02/15/MicroFusion-in-Intel-CPUs, 他说:

enter image description here Unlamination for SandyBridge is described in Intel® 64 and IA-32 Architectures Optimization Reference Manual in chapter “2.3.2.4: Micro-op Queue and the Loop Stream Detector (LSD)”:

微操作队列为某些指令类型提供解码后功能。特别是,与计算操作和所有存储相结合的加载,当与索引寻址一起使用时,在解码器或解码 ICache 中表示为单个微操作。在微操作队列中,它们通过称为取消分层的过程被分成两个微操作,一个执行加载,另一个执行操作

And in 黑客新闻主题 https://news.ycombinator.com/item?id=16304415#16305542,BeeOnRope 指出:

当指令在解码时融合,但在重命名之前“未层压”时,它通常具有与根本不融合类似的性能(但它确实节省了 uop 缓存中的空间),因为 RAT 更有可能成为性能限制。

在这种情况下,为什么要使用未层压代替指令解码时使用更多μops?看起来没有必要吗?

还是因为给定的μop是否应该unlamination在解码阶段是不确定的,需要根据运行时的CPU使用情况动态确定?


微融合+非层压在大部分前端都获得了微融合的吞吐量优势,只是在问题/重命名时失去了它。如果没有这种好处,更多的代码可能会在管道的早期部分遇到瓶颈,尤其是传统解码,其中任何多微指令指令都必须在一个“复杂”解码器中解码,而不是在任何“简单”解码器中解码。https://www.realworldtech.com/sandy-bridge/4/ https://www.realworldtech.com/sandy-bridge/4/


Sandybridge-family simplified the uop format for the out-of-order parts of the back-end (ROB and RS)1; fewer transistors for the same number of ROB entries saves power in a power-intensive part of the CPU. The ROB has to keep track of whether both uops have finished executing, and is dealing with physical register numbers since register-rename has already happened on issue/rename/allocate.

对我来说,解码是值得的vaddps ymm0, ymm1, [rdi+rdx*4]到解码器和微指令缓存中的单个微指令,然后取消层压,而不是首先不熔合。

在解码器中,只有一个复杂解码器可以产生超过 1 个 uop,因此任何尚未恰好位于其解码组中第一个的多 uop 指令都会提前结束该组。使用索引寻址模式拥有一堆带有内存操作数的指令可能会削弱传统解码吞吐量,因为每个这样的指令都会自行解码,需要复杂的解码器。

在uop缓存中,节省空间是有意义的;每“行”6 个 uop 并不是很大,多条指令的额外 uop 很容易需要同一 32 字节块的额外“行”,从而降低缓存密度,从而降低命中率。与 ROB 不同的是,它只需要作为块的一部分获取,无需索引即可让完成端口将其标记为“完成”并准备退出。


英特尔确实对 Haswell 进行了更改,以允许保持更多指令微融合:具有 2 个操作数和读+写目标的指令可以保持索引寻址模式微融合,例如addps xmm0, [rdi + rdx*4]。但不是vaddps xmm0, xmm0, [rdi+rdx*4], 很遗憾。看微融合和寻址模式 https://stackoverflow.com/questions/26046634/micro-fusion-and-addressing-modes

因此,显然他们意识到或决定值得在 ROB 条目上多花一些位来减少大量代码中的未分层。很多时候 CPU 都在运行标量代码,其指令如下add rdx, [rsi+rcx] or mov [rdi + rcx*4], eax(在 Intel CPU 上,存储是存储地址 + 存储数据微指令,每个写入存储缓冲区条目的一部分),而不是 AVX。此外,Haswell uop 格式必须更改以适应具有 3 个输入的单 uop FMA;在此之前,英特尔微指令最多可以有 2 个输入。 (直到布罗德韦尔,他们才利用这一点来制作adc and cmov单微操作;也许他们希望通过微代码禁用 FMA 作为一个选项,以防发现错误,因此不想将其硬连接到一些基线 x86 指令的处理方式中,这些指令无法在需要运行的 CPU 中禁用现有的二进制文件。)


还是因为给定的μop是否应该unlamination在解码阶段是不确定的,需要根据运行时的CPU使用情况动态确定?

也许与这个想法有关;在预解码中,指令被引导至适当的解码器。一些操作码总是被引导到复杂的解码器,将它们限制为传统解码的 1/时钟吞吐量,即使该操作码的实例实际上解码为单个 uop。 (至少这是我们最好的解释理论最近的英特尔微架构中的简单解码器可以处理所有 1-μop 指令吗? https://stackoverflow.com/questions/61980149/can-the-simple-decoders-in-recent-intel-microarchitectures-handle-all-1-%C2%B5op-inst)

如果预解码器必须基于索引寻址模式转向复杂解码器,它们可能会做一些不幸的事情,例如将带有 SIB 的任何 uop 发送到复杂解码器,包括add eax, [rsp+16].

它可能还使部分解码器与 Nehalem 更加相似,如果该指令可能的话,无论寻址模式如何,总是微融合内存操作数。


脚注 1:我不记得在哪里读到过有关英特尔简化后端内部微指令格式的事实。它不在https://www.realworldtech.com/sandy-bridge/ https://www.realworldtech.com/sandy-bridge/所以也许在https://agner.org/optimize/ https://agner.org/optimize/(微架构指南)

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

为什么需要对 μop 进行分层? 的相关文章

  • 为什么这个函数在额外读取内存时运行速度如此之快?

    我目前正在尝试了解 x86 64 上某些循环的性能属性 特别是我的 Intel R Core TM i3 8145U CPU 2 10GHz 处理器 具体来说 在循环体内添加一条额外的指令来读取内存几乎可以使性能提高一倍 而细节并不是特别重
  • 找出 CPU 时钟频率(每个内核、每个处理器)

    像 CPUz 这样的程序非常擅长提供有关系统的深入信息 总线速度 内存时序等 但是 是否有一种编程方法可以计算每个核心 以及每个处理器 在每个 CPU 具有多个核心的多处理器系统中 频率 而无需处理 CPU 特定信息 我正在尝试开发一个反作
  • 气体:内存引用太多

    编译时指令如下 movl 4 ebp 8 ebp I got 内存引用过多 它出什么问题了 括号之前的数字是字节偏移量 这会导致发生内存引用 并且不能有两个movl 您需要先将值暂时移至寄存器 movl 4 ebp ecx movl ecx
  • 优化算术编码器

    我正在优化名为的 C 库的编码步骤PackJPG http www elektronik htw aalen de packjpg 我使用 Intel VTune 对代码进行了分析 发现当前的瓶颈是 PackJPG 使用的算术编码器中的以下
  • Python:获取Windows操作系统版本和架构

    首先 我不认为这个问题是重复的 在Python中检测64位操作系统 windows https stackoverflow com questions 2208828 detect 64bit os windows in python因为恕
  • 任何浮点密集型代码是否会在任何基于 x86 的架构中产生位精确的结果?

    我想知道使用浮点运算的 C 或 C 代码是否会在任何基于 x86 的体系结构中产生位精确的结果 无论代码的复杂性如何 据我所知 自 Intel 8087 以来的任何 x86 架构都使用准备处理 IEEE 754 浮点数的 FPU 单元 并且
  • 如何在 x86 汇编中编写自修改代码

    我正在考虑为我最近开发的一个业余爱好虚拟机编写一个 JIT 编译器 我了解一些汇编语言 我主要是一名 C 程序员 我可以阅读大多数汇编语言并参考我不理解的操作码 并编写一些简单的程序 但是我很难理解这几个示例我在网上找到的自我修改代码 这是
  • Nodejs 异步函数是否使用所有 CPU 核心?

    如果我使用异步函数或带有回调的函数 例如本机 fs 模块 http 等 它们会默认在所有 cpu 核心上运行吗 或者整个系统只使用 1 个核心 Node js 中的一些异步操作 例如文件 I O fsmodule 将通过 libuv 中的线
  • 如何让 gcc 生成合适的代码来检查缓冲区是否充满 NUL 字节?

    我正在实现一个解析磁带档案的程序 解析器逻辑的一部分是检查存档结束标记 该标记是一个充满 NUL 字节的 512 字节块 我为此编写了以下代码 希望 gcc 能对此进行很好的优化 int is eof block const char us
  • 添加饱和 32 位有符号整数内在函数?

    有人可以推荐一种使用 Intel 内在函数 AVX SSE4 添加饱和 32 位有符号整数的快速方法吗 我查看了内在指南并发现 mm256 adds epi16但这似乎只添加 16 位整数 我没有看到 32 位有任何类似的东西 其他电话似乎
  • 汇编:使用数据段寄存器(DS)

    目前我正在学习 x86 汇编 因为我喜欢微控制器编程 所以我熟悉汇编 目前我一直在到处寻找这个问题的答案 但似乎找不到它 DS寄存器 我知道它应该指向我程序中的全局数据 但我不知道知道它到底是如何工作的 我正在使用 NASM 在大多数简单的
  • 调用/返回/jmp等后x86代码执行?

    我希望这个问题不会太愚蠢 因为它看起来似乎很明显 当我对缓冲区溢出进行一些研究时 我偶然发现了一个简单的问题 调用 返回 跳转后转到新指令地址后 CPU是否会执行该地址处的OP代码 然后将一个字节移动到下一个地址并执行下一个OP代码 依此类
  • 二元炸弹 - 第 6 阶段

    这是拆解phase 6 08048dbf
  • Visual Studio:如何正确构建和指定 x64 和 x86 的配置和平台

    使用 Visual Studio 2012 Professional 和 Ultimate 以及所有最新更新 如何正确指定配置和平台以正确构建 x86 和 x64 当您第一次创建 Winforms 应用程序时 Visual Studio 会
  • 将 1 字节立即值添加到 2 字节内存位置

    The add说明文档来 自这一页 http x86 renejeschke de html file module x86 id 5 html说如下 请注意我突出显示的两条说明 我在 NASM 中尝试了以下代码 符合第一个突出显示的指令
  • 就分页分段内存而言的程序寿命

    我对 x86 Linux 机器中的分段和分页过程有一个令人困惑的概念 如果有人能澄清从开始到结束所涉及的所有步骤 我们将很高兴 x86 使用分页分段内存技术进行内存管理 任何人都可以解释一下从可执行的 elf 格式文件从硬盘加载到主内存到它
  • C++使用太多CPU

    好的 我正在创建一个游戏 但它使用了太多的 cpu 但它没有使用太多的内存 cpu确实增加和减少 我的游戏中有太多计时器 当我不再使用计时器时 我会杀死计时器 因此这应该会导致问题 但我认为导致问题的原因是我的消息队列中有太多消息 我有一台
  • 汇编PC相对寻址模式

    我正在研究数据路径 并一直在尝试理解分支指令 这就是我的理解 在 MIPS 中 每条指令都是 32 位 这是 4 个字节 所以下一条指令将是四个字节之外 举个例子 我说PC地址是128 我的第一个问题是理解这个128意味着什么 我目前的信念
  • 无法理解寄存器和变量之间的汇编mov指令

    我在 64 位 Linux 上使用 NASM 汇编器 有一些关于变量和寄存器的东西我无法理解 我创建一个名为 msg 的变量 msg db hello world 现在 当我想写入标准输出时 我移动msg to rsi注册 但我不明白mov
  • x86-64 AMD 上 CALL 指令的操作数生成

    以下是示例程序 objdump 的输出 080483b4

随机推荐