【问题标题】:Compare two __m128i values for total order比较两个 __m128i 值以获得总订单
【发布时间】:2019-10-13 22:28:41
【问题描述】:

我需要一种方法来比较 C++ 中 __m128i 类型的值,以获得 __m128i 类型的任何值之间的总顺序。订单类型无关紧要,只要它在__m128i 类型的所有值之间建立总订单即可。因此,只要提供总顺序,比较可能会小于 128 位整数或完全其他的东西。

我尝试使用< 运算符,但它没有返回bool,而是似乎比较了__m128i 的向量分量(即SIMD):

#include <emmintrin.h>

inline bool isLessThan(__m128i a, __m128i b) noexcept {
     // error: cannot convert '__vector(2) long int' to 'bool' in return
     return a < b;
}

另一种可能性是使用memcmp/strcmp 或类似的,但这很可能不是最佳的。针对至少具有 SSE4.2 和 AVX2 的现代 Intel x86-64 CPU,是否有任何内在函数/指令可以用于此类比较?怎么做?

PS:类似的问题已经被问到检查相等但不是为了排序:

【问题讨论】:

  • 一个简单的解决方案(使用 gcc/clang)将转换为 __int128 并与 &lt; 进行比较:godbolt.org/z/Kh0Yd5。不一定是最有效的(但这也取决于您的输入通常来自寄存器还是内存)

标签: c++ x86 x86-64 simd intrinsics


【解决方案1】:

给你。

inline bool isLessThan( __m128i a, __m128i b )
{
    /* Compare 8-bit lanes for ( a < b ), store the bits in the low 16 bits of the
       scalar value: */
    const int less = _mm_movemask_epi8( _mm_cmplt_epi8( a, b ) );

    /* Compare 8-bit lanes for ( a > b ), store the bits in the low 16 bits of the
       scalar value: */
    const int greater = _mm_movemask_epi8( _mm_cmpgt_epi8( a, b ) );

    /* It's counter-intuitive, but this scalar comparison does the right thing.
       Essentially, integer comparison searches for the most significant bit that
       differs... */
    return less > greater;
}

顺序不太理想,因为pcmpgtb 将这些字节视为有符号整数,但您说这对您的用例并不重要。


更新:这是一个稍慢的版本,带有uint128_t 排序顺序。

// True if a < b, for unsigned 128 bit integers
inline bool cmplt_u128( __m128i a, __m128i b )
{
    // Flip the sign bits in both arguments.
    // Transforms 0 into -128 = minimum for signed bytes,
    // 0xFF into +127 = maximum for signed bytes
    const __m128i signBits = _mm_set1_epi8( (char)0x80 );
    a = _mm_xor_si128( a, signBits );
    b = _mm_xor_si128( b, signBits );

    // Now the signed byte comparisons will give the correct order
    const int less = _mm_movemask_epi8( _mm_cmplt_epi8( a, b ) );
    const int greater = _mm_movemask_epi8( _mm_cmpgt_epi8( a, b ) );
    return less > greater;
}

我们通过将无符号输入范围移位为有符号(通过翻转高位 = 减 128)来构建无符号比较。

【讨论】:

  • 我试过这个,但它会生成糟糕的程序集,不是吗?还是意料之中?
  • 您可以将-Gv 与MSVC 一起使用,因此它使用__vectorcall,因此您的源代码仍然可以与GCC 编译。 (并且不会覆盖 clang 的 x86-64 System V 调用约定。)
  • 我们可以用pcmpgtq 做任何有用的事情,然后用高元素洗牌+屏蔽低元素吗?不过那可能更贵; pmovmskbmovmskpd 很便宜,最坏的推土机系列只有 2 微秒。 (Bulldozer 系列中 xmm->int 的高延迟对任何一种方式都有同样的伤害,并且两者都可以并行发生。)
猜你喜欢
  • 2018-04-18
  • 1970-01-01
  • 2013-12-08
  • 2018-11-06
  • 1970-01-01
  • 2019-10-07
  • 2016-02-18
  • 2016-03-22
  • 1970-01-01
相关资源
最近更新 更多