完全知道这些完全人为的基准测试并不重要,但我对"大4"编译器选择编写一个简单的片段的几种方式感到有点惊讶.
struct In {
bool in1;
bool in2;
};
void foo(In &in) {
extern bool out1;
extern bool out2;
out1 = (in.in1 == true);
out2 = in.in2;
}
Run Code Online (Sandbox Code Playgroud)
注意:所有编译器都设置为x64模式,具有最高的"通用"(=没有指定特定的处理器体系结构)"优化速度"设置; 你可以通过自己看到的结果/和他们一起玩了,在https://gcc.godbolt.org/z/K_i8h9)
带有-O3的Clang 6似乎产生了最直接的输出:
foo(In&): # @foo(In&)
mov al, byte ptr [rdi]
mov byte ptr [rip + out1], al
mov al, byte ptr [rdi + 1]
mov byte ptr [rip + out2], al
ret
Run Code Online (Sandbox Code Playgroud)
在符合标准的C++程序中,== true比较是多余的,因此两个分配都成为从一个内存位置到另一个内存位置的直接副本,al因为内存没有内存mov.
但是,由于这里没有寄存器压力,我原本期望它使用两个不同的寄存器(完全避免两个赋值之间的错误依赖链),可能先启动所有读操作,然后执行所有写操作,以帮助指令级并行; 由于寄存器重命名和积极无序的CPU,这种优化是否已经完全淘汰了最近的CPU?(稍后会详细介绍)
带有-O3的GCC 8.2 …
我目前正在查看CPU管道的各个部分,它们可以检测分支错误预测.我发现这些是:
我知道2和3检测到了什么,但我不明白在BTB中检测到了什么错误预测.BAC检测BTB错误地预测非分支指令的分支的位置,其中BTB未能检测到分支,或者BTB错误预测了x86 RET指令的目标地址.执行单元评估分支并确定它是否正确.
在分支目标缓冲区中检测到什么类型的错误预测?究竟在这里发现了什么错误预测?
我能找到的唯一线索是英特尔开发者手册第3卷(底部的两个BPU CLEAR事件计数器):

BPU在错误地认为未采取分支后预测了一个分支.
这似乎暗示预测并非"同步",而是"异步",因此"在错误地假设"之后?
更新:
Ross,这是CPU分支电路,来自最初的英特尔专利(如何用于"阅读"?):

我在任何地方都看不到"分支预测单位"?读过这篇论文的人会认为"BPU"是将BTB电路,BTB缓存,BAC和RSB分组在一起的懒惰方式吗?
所以我的问题仍然存在,哪个组件会引发BPU CLEAR信号?
optimization intel cpu-architecture computer-architecture branch-prediction
在最近的 Intel CPU 上,POP指令的吞吐量通常为每个周期 2 条指令。但是,当使用寄存器R12(或者RSP,除了前缀之外具有相同编码)时,如果指令通过传统解码器,吞吐量会下降到每个周期 1(如果 μops 来自 DSB,吞吐量保持在每个周期大约 2 )。
这可以使用nanoBench重现,如下所示:
sudo ./nanoBench.sh -asm "pop R12"
Run Code Online (Sandbox Code Playgroud)
在 Haswell 机器上的进一步实验表明:当在 1 和 4 之间添加时nops,
sudo ./nanoBench.sh -asm "pop R12; nop;"
sudo ./nanoBench.sh -asm "pop R12; nop; nop;"
sudo ./nanoBench.sh -asm "pop R12; nop; nop; nop;"
sudo ./nanoBench.sh -asm "pop R12; nop; nop; nop; nop;"
Run Code Online (Sandbox Code Playgroud)
执行时间增加到 2 个周期。添加第 5 个时nop,
sudo ./nanoBench.sh -asm "pop R12; nop; nop; nop; …Run Code Online (Sandbox Code Playgroud) 尽管单词的常见定义(如维基百科所述)是:
用于指定存储器中的位置的最大可能地址大小通常是硬件字(这里,“硬件字”是指处理器的全尺寸自然字,而不是使用的任何其他定义)。
根据一些消息来源,x86 系统注意到它被视为 16 位:
在 x86 PC(Intel、AMD 等)中,虽然架构很早就支持 32 位和 64 位寄存器,但其本机字大小可以追溯到 16 位起源,“单个”字为 16 位。“双”字是 32 位。请参阅 32 位计算机和 64 位计算机。
然而英特尔的官方文档(sdm 第 2 卷,第 1.3.1 节)指出:
这意味着字的字节从最低有效字节开始编号。图 1-1 说明了这些约定。
图 1-1 显示了 x86-64 上下文中单词的小端序列中的 4 个字节,而不是 2 个字节或 8 个字节(如上面链接的来源的不同定义所建议的那样):
我对这一切真正感到困惑的是如何获取和解析指令。我正在编写一个模拟器,一旦我解析 PE 格式的可执行文件并进入文本部分,如果我要遵循 4 字节小端格式,这是否意味着将首先解析第 4 个字节?
让我们组成一些字节,例如:
.text segment buffer:
< 0x10, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 > ....
Run Code Online (Sandbox Code Playgroud)
我会将第一条指令解析为 1C、1B、1A、10、20、1F、1E、1D ...(等等,由于长度可变,显然可能有更多的单词需要读取,具体取决于这里的实际字节是什么)?
我刚刚查看了彼得·科德斯(Peter Cordes)的回答,他说,
如果读取标志,则部分标志停顿会发生,如果它们确实发生的话。P4永远不会有部分标志停顿,因为它们永远不需要合并。相反,它具有错误的依赖关系。几个答案/评论混淆了术语。它们描述了一个错误的依赖关系,但随后将其称为部分标志停顿。这是由于仅写入一些标志而导致的速度下降,但是术语“部分标志停顿”是指必须合并部分标志写入时在SnB之前的Intel硬件上发生的情况。英特尔SnB系列CPU插入一个额外的uop来合并标志而不会停顿。Nehalem和更早的失速约7个周期。我不确定AMD CPU会受到多大的损失。
我感觉我还不明白什么是“部分国旗摊位”。我怎么知道一个人发生了?除了读取标志的某些时间之外,什么触发事件?合并标志是什么意思?在什么情况下会“写一些标志”,但不会发生部分标志合并?我需要了解哪些有关旗位的知识才能理解它们?
在“Intel CPU 中的 MicroFusion”中。丹尼斯·巴赫瓦洛夫 (Denis Bakhvalov)说道:
\n
\nSandyBridge 的取消层压在《Intel\xc2\xae 64 和 IA-32 架构优化参考手册》章节 \xe2\x80\x9c2.3.2.4:微操作队列和循环流检测器 (LSD)\xe2\x80 中进行了描述\x9d:
\n\n微操作队列为某些指令类型提供解码后功能。特别是,与计算操作和所有存储相结合的加载,当与索引寻址一起使用时,在解码器或解码 ICache 中表示为单个微操作。在微操作队列中,它们通过称为取消分层的过程被分成两个微操作,一个执行加载,另一个执行操作
\n
BeeOnRope在HackerNews 主题中指出:
\n\n\n当指令在解码时融合,但在重命名之前为 \xe2\x80\x9cunlaminate\xe2\x80\x9d 时,它通常具有与根本不融合相似的性能(但它确实节省了 uop 缓存中的空间),因为 RAT 更可能是性能限制。
\n
在这种情况下,为什么在指令解码时使用unlamination而不是使用更多 \xce\xbcops使用更多的 \xce\xbcops ?看起来没有必要吗?
\n还是因为给定的\xce\xbcop是否应该unlamination在解码阶段是不确定的,需要根据运行时的CPU使用情况动态确定?
\n我了解的是,指令融合有两种类型:
微操作是指可以在1个时钟周期内执行的操作。如果几个微操作融合在一起,我们将获得一个“指令”。
如果融合了多条指令,我们将获得宏操作。
如果几个宏操作融合在一起,我们将获得宏操作融合。
我对么?
x86 ×5
intel ×4
assembly ×3
c++ ×1
cpu ×1
cpu-word ×1
endianness ×1
machine-code ×1
optimization ×1
performance ×1
x86-64 ×1