【问题标题】:Getting max value in a __m128i vector with SSE?使用 SSE 在 __m128i 向量中获取最大值?
【发布时间】:2012-04-10 06:51:18
【问题描述】:

我刚刚开始使用 SSE,我很困惑如何获得 __m128i 的最大整数值 (max)。例如:

__m128i t = _mm_setr_ps(0,1,2,3);
// max(t) = 3;

四处搜索使我找到了MAXPS 指令,但我似乎无法找到如何将它与"xmmintrin.h" 一起使用。

此外,您是否会推荐任何有关 "xmmintrin.h" 的文档,而不是查看头文件本身?

【问题讨论】:

  • 您需要的随机播放与水平总和或几乎任何其他水平缩减相同。请参阅stackoverflow.com/questions/6996764/… 了解浮点、整数和双精度的一些优化版本,以及 SSE2、SSE3 和 AVX。还讨论了哪些 shuffle 在哪些 CPU 上是最佳的。
  • 这个问题似乎对浮点数和整数感到困惑。 __m128i 是一个整数向量。 *_psMAXPS 是打包单浮点数。有关文档,请参阅 the SSE tag wiki 的链接,以及 stackoverflow.com/tags/x86/info 的更多链接。一个非常好的资源是Intel's intrinsics search/finder,其中详细介绍了每个资源的作用,但没有 asm 说明参考手册中的详细信息。

标签: c assembly x86 sse


【解决方案1】:

SSE 中没有水平最大操作码(至少在我停止跟踪新 SSE 指令之前)。

所以你被困在做一些洗牌。你最终得到的是......

movhlps %xmm0, %xmm1            # Move top two floats to lower part of %xmm1
maxps   %xmm1, %xmm0            # Get minimum of sets of two floats
pshufd  $0x55, %xmm0, %xmm1     # Move second float to lower part of %xmm1
maxps   %xmm1, %xmm0            # Get minimum of all four floats originally in %xmm0

http://locklessinc.com/articles/instruction_wishlist/

MSDN 记录了内在和宏函数映射

http://msdn.microsoft.com/en-us/library/t467de55.aspx

【讨论】:

    【解决方案2】:

    根据this page,没有水平最大值,需要垂直测试元素:

    movhlps xmm1,xmm0         ; Move top two floats to lower part of xmm1
    maxps   xmm0,xmm1         ; Get maximum of the two sets of floats
    pshufd  xmm1,xmm0,$55     ; Move second float to lower part of xmm1
    maxps   xmm0,xmm1         ; Get minimum of the two remaining floats
    

    反之,求最小值:

    movhlps xmm1,xmm0
    minps   xmm0,xmm1
    pshufd  xmm1,xmm0,$55
    minps   xmm0,xmm1
    

    【讨论】:

    • pshufd between maxps 指令在许多 CPU(包括 Intel)上具有额外的延迟。 SSE3 movshdup 将在寄存器的每一半中复制上部浮点数,因此您可以使用它来避免 movaps 复制。
    • @PeterCordes,您能编写自己的优化解决方案吗?如果它是浮点数的向量会有所不同吗?谢谢。
    • @Royi:这个答案 is 用于float 的向量(因为这个问题的标题错误或混淆了浮点数与整数,请参阅我的 cmets题)。针对哪些微架构进行了优化,并针对 SSE 的哪个级别进行了优化?上证三?还是仅限于 SSE2?还是AVX2?请参阅stackoverflow.com/questions/6996764/…(但将add 替换为max)了解各种优化的浮点和整数洗牌。
    • 假设 SSE4。针对 Haswell 及更高版本进行了优化。谢谢。 P.S.我的意思是使用SSE Intrinsics,上面的不是Assembly吗?
    • @PeterCordes,我使用了它,可以在这里展示 - codereview.stackexchange.com/questions/177658。这是你的意思吗?知道为什么它仍然这么慢吗?
    【解决方案3】:

    如果您发现自己需要对向量进行水平操作,尤其是当它位于内部循环中时,那么这通常表明您正在以错误的方式处理 SIMD 实现。 SIMD 喜欢在向量上按元素操作 - 如果您愿意,可以“垂直”而不是水平。

    至于文档,有一个 very useful reference on intel.com,其中包含所有操作码和内部函数,从 MMX 到各种风格的 SSE,一直到 AVX 和 AVX-512。

    【讨论】:

    • 感谢您的链接。水平部分仅用于循环条件,但我会修改我的方法
    • @MarkLakata:谢谢-答案已更新-我想念旧的离线指南-以及在没有互联网连接的情况下工作,它也很有用,因为您可以抓取数据以用于其他用途。不过没关系 - 新的在线版本仍然很好。
    【解决方案4】:

    如果有人关心,并且由于这些天内在函数似乎是要走的路,这里是内在函数方面的解决方案。

    int horizontal_max_Vec4i(__m128i x) {
        __m128i max1 = _mm_shuffle_epi32(x, _MM_SHUFFLE(0,0,3,2));
        __m128i max2 = _mm_max_epi32(x,max1);
        __m128i max3 = _mm_shuffle_epi32(max2, _MM_SHUFFLE(0,0,0,1));
        __m128i max4 = _mm_max_epi32(max2,max3);
        return _mm_cvtsi128_si32(max4);
    }
    

    我不知道这是否比这更好:

    int horizontal_max_Vec4i(__m128i x) {
        int result[4] __attribute__((aligned(16))) = {0};
        _mm_store_si128((__m128i *) result, x);
        return max(max(max(result[0], result[1]), result[2]), result[3]); 
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-18
      • 1970-01-01
      • 2012-11-28
      • 1970-01-01
      • 1970-01-01
      • 2014-04-10
      • 1970-01-01
      • 1970-01-01
      • 2014-12-28
      相关资源
      最近更新 更多