当前位置: 首页 > 知识库问答 >
问题:

汇编-如何通过延迟和吞吐量对CPU指令进行评分

解沈义
2023-03-14

我正在寻找一种公式/方法来衡量一条指令的速度,或者更具体地说,按CPU周期给每条指令一个“分数”。

以下面的汇编程序为例,

nop                     
mov         eax,dword ptr [rbp+34h] 
inc         eax     
mov         dword ptr [rbp+34h],eax  

以及以下Intel Skylake信息:

mov r,m:吞吐量=0.5延迟=2

Mov m, r:吞吐量=1延迟=2

nop:吞吐量=0.25延迟=非

inc:吞吐量=0.25延迟=1

我知道程序中的指令顺序在这里很重要,但我希望创建一些不需要“精确到单个周期”的通用指令

有人知道我该怎么做吗?

共有1个答案

淳于泓
2023-03-14

没有可以应用的公式;你必须测量。

同一uarch系列的不同版本上的相同指令可能具有不同的性能。例如mulps

  • Sandybridge 1c/5c吞吐量/延迟。
  • HSW 0.5/5。BDW 0.5/3(FMA单元中更快的乘法路径?FMA仍然是5c)。
  • SKL 0.5/4(FMA延迟也更低)。SKL也在FMA单元上运行addps,删除专用的FP乘法单元,因此添加延迟更高,但吞吐量更高。

如果不测量或不了解一些微体系结构的细节,就无法预测这一切。我们预计FP math操作不会是单周期延迟,因为它们比整数操作复杂得多。(因此,如果它们是单周期,则时钟速度对于整数运算设置得太低。)

您可以通过在展开的循环中多次重复该指令来测量。或者完全展开而没有循环,但随后您击败了uop-cache并可以获得前端瓶颈。(例如,用于解码10字节的mov r64, im64

https://uops.info/已经对每条(非特权)指令的每种形式进行了自动化测试,您甚至可以单击任何表条目来查看他们使用了哪些测试循环。例如Skylakexchg r32,eax延迟测试(https://uops.info/html-lat/SKL/XCHG_R32_EAX-Measurements.html)从每个输入操作数到每个输出。(EAX的2个周期延迟-

https://uops.info/是目前最好的测试数据来源;当它和Agner的表格不一致时,我自己的测量和/或其他来源总是证实uops.info的测试是准确的。他们不会试图为往返的两半(如movd xmm0、eax和back)计算延迟数,他们会向您显示假设链的其余部分是最小合理的可能延迟范围。

Agner Fog通过对重复指令的大型非循环代码块计时来创建他的指令表(您似乎正在读取)。https://agner.org/optimize/.他的指令表的简介部分简要解释了他是如何测量的,他的微体系结构指南解释了不同x86微体系结构如何在内部工作的更多细节。不幸的是,他手工编辑的表格中偶尔会出现打字错误或复制/粘贴错误。

http://instlatx64.atw.hu/也有实验测量的结果。我认为他们使用了一种类似的技术,即重复相同指令的大数据块,可能小到足以放入uop缓存。但它们不使用性能计数器来衡量每条指令需要什么执行端口,因此它们的吞吐量数字不能帮助您确定哪些指令与哪些其他指令竞争。

后两种来源的存在时间比UOP公司更长。信息,并涵盖一些旧的CPU,尤其是旧的AMD。

要自己测量延迟,可以将每条指令的输出作为下一条指令的输入。

 mov  ecx, 10000000
 inc_latency:
     inc eax
     inc eax
     inc eax
     inc eax
     inc eax
     inc eax

     sub ecx,1          ; avoid partial-flag false dep for P4
     jnz inc_latency    ; dec or sub/jnz macro-fuses into 1 uop on Intel SnB-family

这个由7条指令组成的依赖链将在每7个延迟周期进行1次迭代时限制循环。使用核心时钟周期(而非RDTSC周期)的性能计数器,您可以轻松地测量所有迭代到10k中1部分的时间,并且更加小心,甚至可能更精确。10000000的重复计数隐藏了您使用的任何计时的开始/停止开销。

我通常将这样的循环放在Linux静态可执行文件中,它只是直接(使用syscall)指令进行sys_exit(0)系统调用,并使用perf stat./testcle对整个可执行文件计时以获取时间和循环计数。(参见x86的MOV真的是“免费的”吗?为什么我根本不能重现它?举个例子)。

另一个例子是了解lfence对具有两个长依赖链的循环的影响,以增加长度,并增加使用lfence排出两个dep链无序执行窗口的复杂性。

为了测量吞吐量,可以使用单独的寄存器,和/或偶尔包括异或归零,以断开dep链,并让无序的exec重叠。不要忘记使用perf计数器来查看它可以在哪些端口上运行,这样就可以知道它将与哪些其他指令竞争。(例如,FMA(p01)和shuffles(p5)根本不争夺Haswell/Skylake上的后端资源,只争夺前端吞吐量。)不要忘了测量前端uop计数:有些指令解码后会乘以uop。

我们需要多少不同的依赖链来避免瓶颈?我们知道延迟(首先测量),我们知道最大可能吞吐量(执行端口数或前端吞吐量)

例如,如果FP multiply的吞吐量为0.25c(每个时钟4个),那么我们可以在Haswell上一次保持20个(5c延迟)。这比我们拥有的寄存器还多,所以我们可以使用所有16个寄存器,发现实际上吞吐量只有0.5c。但如果16个寄存器是瓶颈,我们可以偶尔添加xorps xmm0、xmm0,并让无序执行重叠一些块。

越多越好;几乎不足以隐藏延迟会因为不完美的调度而变慢。如果我们想疯狂地测量inc,我们会这样做:

 mov  ecx, 10000000
 inc_latency:
   %rep 10          ;; source-level repeat of a block, no runtime branching
     inc eax
     inc ebx
     ; not ecx, we're using it as a loop counter
     inc edx
     inc esi
     inc edi
     inc ebp
     inc r8d
     inc r9d
     inc r10d
     inc r11d
     inc r12d
     inc r13d
     inc r14d
     inc r15d
   %endrep

     sub ecx,1          ; break partial-flag false dep for P4
     jnz inc_latency    ; dec/jnz macro-fuses into 1 uop on Intel SnB-family

如果我们担心部分标志错误依赖或标志合并效应,我们可能会尝试在某个地方混合使用异或eax、eax,让OoO exec重叠,而不仅仅是在写入所有标志时重叠。(参见INC说明vs ADD 1:是否重要?)

在Sandybridge系列上测量shl r32, cl的吞吐量和延迟也有类似的问题:标志依赖链通常与计算无关,但将shl背靠背通过FLAGS和寄存器创建依赖项。(或者对于吞吐量,甚至没有寄存器dep)。

我在Agner Fog的博客上发布了这一消息:https://www.agner.org/optimize/blog/read.php?i=415#860.我将shl edx,cl与四条add edx,1指令混合在一起,以查看添加一条指令时的增量减速,其中标志依赖性不是问题。在SKL上,平均只会多慢1.23个周期,因此该shl的真正延迟成本只有约1.23个周期,而不是2个周期。(我想,由于资源冲突,运行shl的标志合并uop不是整数,也不是1。BMI2 shlx edx、edx、ecx将正好是1c,因为它只是一个uop。)

相关:有关整个代码块(包含不同指令)的静态性能分析,请参阅在预测现代超标量处理器上操作的延迟时需要考虑哪些因素,以及如何手动计算它们?。(它使用“延迟”一词来表示整个计算的端到端延迟,但实际上是询问一些小到足以让OoO exec重叠不同部分的事情,因此指令延迟和吞吐量都很重要。)

加载/存储的Latency=2数字似乎来自Agner Fog的指令表(https://agner.org/optimize/)。不幸的是,它们对于一系列mov rax,[rax]并不准确。如果您将其放入循环中进行测量,您会发现这是4c延迟。

Agner将加载/存储延迟拆分为一些使总存储/加载延迟正确的内容,但由于某些原因,他没有使加载部分等于L1d加载使用延迟(当它来自缓存而不是存储缓冲区时)。(但还要注意,如果加载提供一条ALU指令而不是另一条加载,则延迟为5c。因此,简单寻址模式快速路径仅对纯指针跟踪有帮助。)

 类似资料:
  • 问题内容: 我为Apache Flink写了一个非常简单的Java程序,现在我对测量统计信息感兴趣,例如吞吐量(每秒处理的元组数)和等待时间(程序需要处理每个输入元组的时间)。 我知道Flink公开了一些指标: https://ci.apache.org/projects/flink/flink-docs- release-1.2/monitoring/metrics.html 但是我不确定如何使

  • 我找不到任何关于agner.orgRDRAND指令的延迟或吞吐量的信息。但是,这个处理器存在,所以信息必须在那里。 编辑:实际上,最新的优化手册中提到了此说明。记录如下:

  • 我正在尝试运行Flink流媒体作业。我想确定流处理的延迟和吞吐量。我已启动Kafka代理服务器,并收到来自Kafka的传入消息。如何计算每秒的邮件数(吞吐量)?(如rdd.count。是否有类似的方法来获取传入消息的计数) (完整的场景:我已经通过生产者发送了消息作为Json对象。我在Json对象中添加了一些信息,如名称为字符串和System.currentTimeMills。在流式传输期间,我如

  • 总的来说,我认为我对延迟和吞吐量之间的区别有很好的理解。但是,对于Intel Intrinsics,延迟对指令吞吐量的影响我还不清楚,尤其是在顺序(或几乎顺序)使用多个内在调用时。 例如,让我们考虑: 这有11个延迟,在Haswell处理器上的吞吐量为7。如果我在循环中运行这条指令,我会在11个循环后获得每个循环的连续输出吗?由于这需要一次运行11条指令,并且由于我的吞吐量为7,我是否用完了“执行

  • 我错过了一些基本的东西。CPU管道:在基本层面上,为什么指令需要不同数量的时钟周期才能完成,为什么一些指令在多级CPU中只需要1个周期? 除了明显的“不同的指令需要不同的工作量来完成”,听我说完... 考虑具有大约14级流水线的i7。这需要14个时钟周期才能完成一次运行。AFAIK,这应该意味着整个流水线的延迟为14个时钟。然而事实并非如此。 XOR在1个周期内完成,延迟为1个周期,这表明它没有经

  • 汇总报告中最后一行显示的总吞吐量是否正确?我使用的是Jmeter 2.11 有人能帮我一下吗?