使用英特尔 SSE 执行分支的最佳方式是啥?

Posted

技术标签:

【中文标题】使用英特尔 SSE 执行分支的最佳方式是啥?【英文标题】:What is the best way to perform branching using Intel SSE?使用英特尔 SSE 执行分支的最佳方式是什么? 【发布时间】:2012-03-04 19:43:40 【问题描述】:

我正在编写一个编译器,我必须输出用于浮点值分支条件的代码。例如,要编译这种代码:

if(a <= b)
    //1. DO something
 else 
    //2. Do something else

当 a 和 b 是浮点变量时。如果条件不成立,我只需要跳转到 2,否则下降到 1。我正在考虑在编译器级别进行优化,考虑到 1 和 2 中的内容。

我需要适用于所有比较运算符 >、>=、

我发现进行比较的一种方法是使用 CMPLTSD(以及其他关系运算符的其他等效指令)。但是有了这个,我必须使用一个 SSE 寄存器,特别是对于结果,然后我必须将它的值移动到一个通用寄存器(例如 eax)上,最后将这个值与 0 进行比较。

我还看到 UCOMISD 指令应该正确设置标志,但显然它不像我想象的那样工作。

那么,处理这样的代码的最佳方法是什么?有比我的第一个解决方案更好的说明吗?

最好,我的意思是,这个问题的一般解决方案。如果可能的话,我希望代码的行为方式与对整数进行比较(cmp a、b;jge 标签)时的行为方式相同。当然,我更喜欢最快的指令来实现这一点。

【问题讨论】:

最好的方法取决于你在做什么。例如,//DO something 块内是什么? “最好的方法”往往取决于看全局,而不是试图逐行翻译你的代码。 我在帖子中添加了详细信息以回答您的两个问题。 如果你真的想分支,UCOMISD(实际上是 SSE2)似乎是答案,它有什么问题?无序结果? UCOMISD的问题是根据比较的结果不知道怎么跳转。我尝试用 jle 跳跃,但没有得到预期的结果。我必须使用特殊的条件跳转指令吗? 【参考方案1】:

ucomisd 的条件代码不对应于有符号整数比较代码,而是对应于无符号比较代码(奇偶校验标志中带有“无序”)。我承认,这有点奇怪,但都清楚地记录在案。 &lt;= 的代码如果你真的想分支可能是这样的:

  ucomisd a,b
  ja else     ; greater
  jp else     ; unordered
  ; code for //1 goes here
  jmp end
else:
  ; code for //2 goes here
end:

对于&lt;

jae else   ; greater or equal
jp else    ; unordered

如果你真的想要,我可以将它们全部列出,但你可以查看ucomisd 的条件代码并将它们与你需要的跳转匹配。

【讨论】:

这确实很奇怪......但我想我会在文档中得到它。非常感谢。【参考方案2】:

重要:@harold 的答案几乎完全正确,但有一个微妙的错误方面,可能会在稍后的一个非常重要的边缘情况下让你发疯——NaN 处理与大多数语言(如c++)。

正如@harold 所说,无序比较结果存储在奇偶校验标志中。

然而,无序比较当任何操作数为 NaN 时为真,详见this stack overflow post。这意味着NaN 将小于、等于和大于绝对每个数字包括NaN

因此,如果您希望您的语言与 c++ 的行为相匹配,即与 NaN 的任何比较都返回 false,您需要:

对于&lt;=

ucomisd xmm0, xmm1
jbe else_label

对于&lt;

ucomisd xmm0, xmm1
jb else_label

在下面的gcc反汇编中确认,我return a &gt;= b

144e:       66 0f 2e c8             ucomisd %xmm0,%xmm1
1452:       0f 93 c0                setae  %al

这里它使用setae,它相当于jae的寄存器修改。然后它立即返回而不检查奇偶校验标志。

对于为什么是ja而不是jg,@harold 的回答仍然是一个清晰正确的解释。

当然,您不必使用有序比较,如果您绝对希望每个数字都小于、大于、,则可以使用无序比较,如上一个答案所示等于您的程序/语言中的NaN(即使NaN &lt; NaN 也是如此!)。当然,正如您所看到的,它可能会慢一些,因为它需要额外的检查。

【讨论】:

以上是关于使用英特尔 SSE 执行分支的最佳方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用快速英特尔随机生成器(SSE2)失败,堆栈周围...已损坏

如何将参数传递给英特尔 SSE 内在函数中的 const 值?

SSE 程序在 AMD 上比在 Intel 上花费的时间要长得多

指令集的相关问题!

not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA

SSE 和超线程