不同类型的逻辑SSE内在之间是否有任何区别?例如,如果我们进行或操作,则有三个内在的:_mm_or_ps,_mm_or_pd和_mm_or_si128所有这些都做同样的事情:计算 钻头 或他们的操作数。我的问题:

  1. 使用一个或另一个固有的(带有适当的类型铸件)之间是否有任何区别。在某些特定情况下,不会有任何隐藏成本,例如更长的执行?

  2. 这些内在映射到三个不同的X86指令(POR,ORP,ORPD)。有人有什么想法,为什么英特尔正在浪费珍贵的操作码空间来进行相同事情的几个说明?

有帮助吗?

解决方案

我认为这三个实际上都是相同的,即128位位操作。存在不同形式的原因可能是历史性的,但我不确定。我想是 可能的 在浮点版本中可能会有一些其他行为,例如何时有NAN,但这是纯粹的猜测。对于普通输入,指令似乎是可以互换的

#include <stdio.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <xmmintrin.h>

int main(void)
{
    __m128i a = _mm_set1_epi32(1);
    __m128i b = _mm_set1_epi32(2);
    __m128i c = _mm_or_si128(a, b);

    __m128 x = _mm_set1_ps(1.25f);
    __m128 y = _mm_set1_ps(1.5f);
    __m128 z = _mm_or_ps(x, y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    c = (__m128i)_mm_or_ps((__m128)a, (__m128)b);
    z = (__m128)_mm_or_si128((__m128i)x, (__m128i)y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    return 0;
}

$ gcc -Wall -msse3 por.c -o por

$ ./por

a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000
a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000

其他提示

  1. 使用一个或另一个固有的(带有适当的类型铸件)之间是否有任何区别。在某些特定情况下,不会有任何隐藏成本,例如更长的执行?

是的,可能有绩效原因选择一个与另一个相对于另一个。

1: 有时,如果需要将整数执行单元的输出路由到FP执行单元的输入,反之亦然,则有一个额外的循环(转发延迟)(转发延迟),反之亦然。将128B数据移动到许多可能的目的地中,需要大量电线,因此CPU设计人员必须进行权衡,例如只有从每个FP输出到每个FP输入的直接路径,而不是所有可能的输入。

这个答案, , 或者 Agner Fog的微观结构文档 绕行案。在Agner的文档中搜索“ Nehalem上的数据旁路延迟”;它有一些很好的实践例子和讨论。他为他分析的每个微批准都有一个部分。

但是,在不同域或不同类型的寄存器之间传递数据的延迟在沙桥和常春藤桥上比尼哈勒姆上的延迟小,通常为零。 - Agner Fog的Micro Arch Doc

请记住,延迟是否不在代码的关键路径上都没有关系。使用 pshufd 代替 movaps + shufps 如果UOP吞吐量是您的瓶颈,而不是关键路径的延迟,则可以是胜利。

2:...ps 版本的代码比其他两个要少1个。这将以不同的方式对齐以下说明,这对于解码器和/或UOP缓存线可能很重要。

3: 最近的Intel CPU只能在Port5上运行FP版本。

  • Merom(Core2)和Penryn: orps 可以在P0/P1/P5上运行,但只能在整数域上运行。大概所有3个版本都解码为完全相同的UOP。因此,跨域转发延迟发生了。 (AMD CPU也这样做:FP位指令在IVEC域中运行。)

  • Nehalem / Sandybridge / IVB / Haswell / Broadwell: por 可以在P0/P1/P5上运行,但是 orps 只能在port5上运行。洗牌也需要P5,但是FMA,FP ADD和FP MUL单元位于端口0/1上。

  • Skylake: pororps 两者都有3个周期的吞吐量. 。有关转发延迟的信息尚不可用。

请注意,在SNB/IVB(AVX而不是AVX2)上,只有P5才需要处理256B逻辑操作,如 vpor ymm, ymm 需要AVX2。这可能不是变化的原因,因为尼哈莱姆这样做了。

如何明智选择:

如果port5上的逻辑OP吞吐量可能是瓶颈,则甚至在FP数据上使用整数版本。如果您想使用整数散装或其他数据移动说明,则尤其如此。

AMD CPU始终将整数域用于逻辑域,因此,如果您有多个整数要做的事情,请立即执行所有操作以最大程度地减少域之间的往返。较短的潜伏期将使重新订购缓冲液清除,即使DEP链不是代码的瓶颈。

如果您只想在fp add和mul指令之间设置/清除/翻转fp向量,请使用 ...ps 逻辑,即使在双重精确数据上,由于单个和双FP是存在的每个CPU的域,并且是相同的域,并且 ...ps 版本是一个短字节。

有实用 /人因素的原因 ...pd 不过,版本通常会超过保存1个字节的代码。其他人对您的代码的可读性是一个因素:他们会想知道为什么您将数据视为单身的两倍时将其视为双打。特别是使用C/C ++固有的内容,将代码乱七八糟地乱七八糟 __mm256__mm256d 不值得。如果调整INSN对齐的水平很重要,请直接写入ASM,而不是内在的! (拥有一个字节更长的指令可能会使UOP缓存线密度和/或解码器更好地对齐。)

对于整数数据,请使用整数版本。保存一个指令字节并不值得旁路延迟,并且整数代码通常会使Port5完全占据混乱。对于Haswell,许多洗牌 /插入 /提取 /包装 /解开指令仅变成P5,而不是SNB / IVB的P1 / P5。

  1. 这些内在的映射到三个不同的x86指令(por, orps, orpd)。有人有什么想法,为什么英特尔正在浪费珍贵的操作码空间来进行相同事情的几个说明?

如果您查看这些说明集的历史记录,您可以看看我们如何到达这里。

por  (MMX):     0F EB /r
orps (SSE):     0F 56 /r
orpd (SSE2): 66 0F 56 /r
por  (SSE2): 66 0F EB /r

MMX存在于SSE之前,因此看起来像SSE的OPCODE(...ps)指示是从同一指示中选择的 0F xx 空间。然后对于SSE2, ...pd 版本添加了 66 操作数大小的前缀 ...ps opcode,整数版添加了一个 66 MMX版本的前缀。

他们 可以 遗漏了 orpd 和/或 por, ,但他们没有。也许他们认为未来的CPU设计可能会在不同域之间具有更长的转发路径,因此使用匹配指令为您的数据使用将是更大的交易。即使有单独的Opcodes,AMD和早期的Intel都将它们与Int-Vector相同。

根据Intel和AMD优化指南,将OP类型与数据类型混合在一起会产生性能,因为CPU内部标记为特定数据类型的寄存器的64位。这似乎主要是在解码指令并安排UOPS的情况下影响管道。在功能上,它们产生相同的结果。整数数据类型的较新版本具有较大的编码,并在代码段中占用了更多空间。因此,如果代码大小是问题,则使用旧操作,因为它们的编码较小。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top