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

为什么sqrtsd指令的延迟会根据输入而变化?英特尔处理器

万俟宜修
2023-03-14

在《英特尔内部指令指南》中指出,名为“sqrtsd”的指令的延迟为18个周期。

我用自己的程序测试了它,例如,如果我们将0.15作为输入,它是正确的。但是当我们取256(或任何2^x)数字时,延迟只有13。为什么?

我的一个理论是,由于13是“sqrtss”的延迟,这与“sqrtsd”相同,但在32位浮点上完成,那么处理器可能足够聪明,能够理解256可以容纳在32位中,因此使用该版本,而0.15需要完整的64位,因为它不能以有限的方式表示。

我使用内联汇编来完成这项工作,这里是使用gcc-O3和-fno树矢量编译的相关部分。

static double sqrtsd (double x) {
    double r;
    __asm__ ("sqrtsd %1, %0" : "=x" (r) : "x" (x));
    return r;
}

共有1个答案

孔深
2023-03-14

SQRT*和DIV*是仅有的两个“简单”ALU指令(单个uop,而不是微编码分支/循环),它们在现代英特尔/AMD处理器上具有与数据相关的吞吐量或延迟。(不包括加/乘/fma中不正常FP值的微码辅助)。其他一切都几乎是固定的,所以无序的uop调度机器不需要等待确认结果已经准备好某个周期,它只知道它会准备好。

与往常一样,英特尔的intrinsics指南给出了一个过于简化的性能图。对于Skylake上的双精度,实际延迟不是固定的18个周期。(根据您选择引用的数字,我假设您有一个Skylake。)

div/sqrt难以实现;即使在硬件中,我们能做的最好的事情就是迭代求精过程。一次细化更多位(自Broadwell以来的基数1024除法器)可以加快速度(参见此Q

https://www.uops.info/html-instr/VSQRTSD_XMM_XMM_XMM.html显示Skylake SQRTSD可以在13到19个周期延迟之间变化。SKL(客户端)数字仅显示13个周期的延迟,但我们可以从详细的SKL vsqrtsd页面中看到,它们仅在输入=0的情况下测试。SKX(服务器)数字显示13-19个周期的延迟。(本页详细介绍了他们使用的测试代码,包括测试的二进制位模式。)在非VEXsqrtsd-xmm,xmm页面上进行了类似测试(客户端核心仅为0)/

InstLatx64结果显示Skylake-X上的最佳/最坏情况延迟为13到18个周期(使用与Skylake客户端相同的内核,但启用了AVX512)。

Agner Fog的指令表显示Skylake上的15-16周期延迟。(Agner通常使用一系列不同的输入值进行测试。)他的测试自动化程度较低,有时与其他结果不完全匹配。

注意,大多数ISA(包括x86)使用二进制浮点:位表示值为线性有效位(也称尾数)乘以2和符号位。

现代英特尔似乎只有两种速度(至少从哈斯韦尔开始)(见评论中与@harold的讨论)e、 g.2的偶数幂都很快,比如0.25、1、4和16。它们的尾数为0x0,表示1.0。https://www.h-schmidt.net/FloatConverter/IEEE754.html有一个很好的交互式小数点

在Skylake上,我在快速检查中发现的唯一快速案例是2的偶数幂,如4.0,但不是2.0。这些数字具有精确的sqrt结果,输入和输出均具有1.0尾数(仅隐式1位集)<代码>9.0不是很快,即使它是可以精确表示的,结果也是如此。3.0的尾数=1.5,只有二进制表示中尾数集的最高有效位。9.0的尾数是1.125(0b00100…)。所以非零位非常接近顶部,但显然这足以取消它的资格。

-InfNaN也很快。普通负数也是如此:结果=-NaN。我在i7-6700k上测量了这些的13个周期延迟,与4.0相同。与慢情况下的18个周期延迟相比。)

x=sqrt(x)绝对快,x=1.0(除隐式前导1位外尾数为全零)。它有一个简单的输入和简单的输出。

对于2.0,输入也很简单(全零尾数和指数1更高),但输出不是整数。sqrt(2)是无理的,因此在任何基数中都有无限的非零位。这显然使它在Skylake上变慢了。

Agner Fog的指令表表示,AMD K10的整数div指令性能取决于被除数(输入)中有效位的数量,而不是商,但搜索Agner的微阵列pdf和指令表时,没有找到任何关于sqrt具体如何依赖于数据的脚注或信息。

在FP sqrt更慢的旧CPU上,可能有更大的速度范围。我认为输入尾数中有效位的数量可能与此相关。如果正确的话,更少的有效位(有效位中更多的尾随零)会使速度更快。但是,在Haswell/Skylake上,唯一快速的情况似乎是2的偶数幂。

您可以使用将输出耦合回输入而不破坏数据依赖性的东西来测试这一点,例如,和ps xmm0、xmm1/或ps xmm0、xmm2,以在xmm0中设置依赖于sqrtsd输出的固定值。

或者测试延迟的一种更简单的方法是利用sqrtsd xmm0、xmm1-it和sqrtss的错误输出依赖关系,使目标的64/32位(分别)保持不变,因此输出寄存器也是该合并的输入。我假设这就是您天真的内联尝试最终导致延迟瓶颈而不是吞吐量瓶颈的原因,编译器为输出选择不同的寄存器,这样它就可以在循环中重新读取相同的输入。您添加到问题中的内联ASM完全损坏,甚至无法编译,但也许您的真实代码使用"x"(xmm寄存器)输入和输出约束而不是"i"(立即)?

静态可执行测试循环(在perf stat下运行)的NASM源使用该伪依赖项和非凸编码sqrtsd。

这一ISA设计优势得益于英特尔在奔腾III上对SSE1进行短期优化。P3在内部将128位寄存器处理为两个64位半寄存器。保持上半部分不变,让标量指令解码为单个uop。(但这仍然给PIII带来了错误的依赖关系)。AVX最终让我们避免了这种情况,至少对于寄存器源,我们使用vsqrtsd dst、src、src,对于类似近视设计的标量int,我们使用类似的vsqrtsd dst、cold_reg、eax-

在许多早期的CPU上,甚至吞吐量都是可变的,但Skylake对分频器进行了足够的增强,使调度器始终知道它可以在最后一个单精度输入之后启动一个新的div/sqrt uop 3周期。

即使Skylake双精度吞吐量也是可变的:如果Agner Fog的指令表是正确的,则在最后一个双精度输入uop之后4到6个周期。https://uops.info/显示了平坦的6c倒数吞吐量。(对于256位向量,长度是原来的两倍;128位和标量可以使用宽SIMD除法器的两半,以获得更高的吞吐量,但延迟相同。)另请参阅浮点除法与浮点乘法,了解从Agner Fog的指令表中提取的一些吞吐量/延迟数。

 类似资料:
  • 问题内容: 从我读到的内容来看,它用于修复CPU中的错误,而无需修改BIOS。根据我对汇编的基本知识,我知道汇编指令在内部由CPU分解为微代码,并相应地执行。但是intel以某种方式可以在系统启动和运行时进行一些更新。 有人有更多信息吗?是否有关于微码可以做什么以及如何使用的文档? 编辑:我读过维基百科的文章:没弄清楚我怎么能自己写一些,以及它有什么用。 问题答案: 在较早的时期,微代码在CPU中

  • 在Barry B. Brey的《智能微处理器》一书中写道 在64位模式下不允许,但在32位或16位模式下允许。如果可以在64位模式下允许MOV AL,1,那么MOV AH,1有什么问题?

  • 问题内容: 我很难处理Java垃圾回收问题并解释日志。 我的应用程序要求GC的时间不要超过2秒,理想情况下是少于100ms。 根据先前的一些建议,我正在尝试以下命令行选项: 该应用程序具有大量长期存储的对象,这些对象保存在ConcurrentLinkedHashMap中。我偶尔会出现长时间的停顿,在最坏的情况下可能会长达10秒(这是倒数第二次,如下面的GC日志所示)! 这是我得到的一些输出: 我已

  • 问题内容: 我有一个输入,可以根据更改过滤ng-repeat列表。重复数据包含大量数据,并且需要花费几秒钟来过滤所有内容。我希望他们在开始过滤过程之前有0.5秒的延迟。 产生延迟的正确方法是什么? 输入项 重复 过滤功能 谢谢 问题答案: AngularJS 1.3+ 从AngularJS 1.3开始,您可以利用提供的属性轻松实现这一点,而无需使用。这是一个例子: HTML: JS: - 要么 -

  • 如何在Vertx中处理延迟作业列表(实际上是数百个HTTP GET请求,到禁止快速请求主机的有限API)?现在,我正在使用此代码,它被阻止,因为Vertx一次启动所有请求。希望在每个请求之间有5秒的延迟来处理每个请求。

  • 我在计算一个简单蒸汽的最大值,结果是: (S11000,S1,值:999) (S12000,S1,值:41) 最后一行数据明显迟到了: 为什么按第一个窗口(0-1000)计算? 我认为第一个窗口应该在到达时触发。 对于这个结果,我很疑惑。 MyReductingMax(),MyWindowFunction()