【问题标题】:SSE2 intrinsics - comparing 2 __m128i's containing 4 int32's each to see how many are equalSSE2 内在函数 - 比较 2 个 __m128i,每个包含 4 个 int32,看看有多少相等
【发布时间】:2014-08-29 15:54:08
【问题描述】:

我是第一次深入了解 SSE2 内在函数,但我不知道该怎么做。

我想将 4 个 int32 与 4 个其他 int32 进行比较,并计算有多少是相等的。 所以我读取了我的前 4 个 int32,将它们设置在 __m128i 中,对第二组执行相同操作,并使用 _mm_cmpeq_epi32 进行比较。

这将导致包含 4 个 int32 的 __m128i,每个 0xffffffff 或 0 取决于 int 是否相等。

但我不知道如何从生成的 __m128i 到 count 指定实际相等的数量。

谁能指出我正确的方向?

我正在拼凑的代码:

        int* source = blah;
        int* reference = otherblah;

        // Load the 4 source int32's (they are actually 4 int32s apart)
        __m128i first_4_int32s = _mm_set_epi32(*(source + 12), *(source + 8), *(source + 4), *(source));

        // Load the 4 source int32's (also actually 4 int32s apart)
        __m128i second_4_int32s = _mm_set_epi32(*(reference + 12), *(reference + 8), *(reference + 4), *(reference));

        // Compare the int32's
        __m128i result = _mm_cmpeq_epi32(first_4_int32s, second_4_int32s);

        // Perform magic here that counts whether 0, 1, 2, 3 or all 4 ints were equal ?!?!

【问题讨论】:

  • 请注意,像这样执行收集的负载会导致性能下降 - 如果您不能使用连续数据,最好还是坚持使用标量代码。
  • 是的,您在这里执行的操作可能比按顺序执行要慢很多。因此,除非您可以更改内存访问模式,否则不要浪费时间对其进行矢量化。
  • 您是只为 4 个值执行此操作,还是为一大堆值,但一次只为 4 个?让我知道,我很乐意写一个解决方案。除此之外,这里有几点。如果真的只有 4 个值,请考虑 _mm_packs_epi32,然后是 _mm_packs_epi16,然后是 _mm_movemask_epi8,然后是查找表,但标量版本可能更快。无论如何,您不应该像那样使用 _mm_set_epi32,而应该使用 _mm_load_ps 或 _mm_loadu_ps 将所有 4 个值一起加载。现在,如果您要为一大堆 4 元组执行此操作,您只需在 __m128i 中维护 4 个总和。
  • 然后最后你可以将 4 个组件加起来为一个值。这可以通过移位和水平添加或减去单个组件来完成。无论如何,它只在最后完成一次。在分别对所有内容求和时,比较后您有两个可能的值。 0x00000000 或 0xFFFFFFFF。请记住 0xFFFFFFFF 也是-1。因此,您可以做的是使用 _mm_sub_epi32 从当前总和中减去比较值。因此,电流减 0 保持电流不变。而电流减-1变成电流加1(减负就是加正)。

标签: count comparison sse intrinsics sse2


【解决方案1】:

您可以将比较结果与一个向量相乘,以创建一个由 0 和 1 组成的向量。然后使用水平加法运算来计数。这里有一些可能性。

#include "stdio.h"
#include "stdint.h"
#include "intrin.h"

//----------------------------------------------------------------------------
// non-SSE method (reference for result check)
static int method0 (__m128i value)
    {
    int index, total = 0;
    uint32_t *buffer = (void *) &value;

    for (index = 0; index < 4; index++)
        total += buffer [index] == 0xFFFFFFFF;
    return total;
    }

//----------------------------------------------------------------------------
//
// horizontalAddBytes - return integer total of all 16 bytes in xmm argument
//
static int horizontalAddBytes (__m128i byteArray)
   {
   __m128i total;
   const __m128i zero = _mm_setzero_si128 ();

   total = _mm_sad_epu8 (byteArray, zero);
   return _mm_cvtsi128_si64 (_mm_add_epi32 (total, _mm_shuffle_epi32 (total, 0xAA)));
   }

//----------------------------------------------------------------------------
// requires SSE2
static int method1 (__m128i value)
    {
    return horizontalAddBytes (_mm_srli_epi32 (value, 31));
    }

//----------------------------------------------------------------------------
// requires SSE3
static int method2 (__m128i value)
    {
    __m128 count;
    const __m128 mask = _mm_set1_ps (1);
    count = _mm_and_ps (_mm_castsi128_ps (value), mask);
    count = _mm_hadd_ps (count, count);
    count = _mm_hadd_ps (count, count);
    return _mm_cvtss_si32 (count);
    }

//----------------------------------------------------------------------------
// requires SSSE3
static int method3 (__m128i value)
    {
    __m128i count;
    count = _mm_srli_epi32 (value, 31);
    count = _mm_hadd_epi32 (count, count);
    count = _mm_hadd_epi32 (count, count);
    return _mm_cvtsi128_si32 (count);
    }

//----------------------------------------------------------------------------

static void createTestData (uint32_t *data, int mask)
    {
    int index;
    for (index = 0; index < 4; index++)
        data [index * 4] = (mask & (1 << index)) != 0;
    }

//----------------------------------------------------------------------------

int main (void)
    {
    int index1, index2, expected, result1, result2, result3;
    uint32_t source [16];
    uint32_t reference [16];

    for (index1 = 0; index1 < 16; index1++)
        for (index2 = 0; index2 < 16; index2++)
            {
            __m128i first_4_int32s, second_4_int32s, result;
            createTestData (source, index1);
            createTestData (reference, index2);

            // Load the 4 source int32's (they are actually 4 int32s apart)
            first_4_int32s = _mm_set_epi32(*(source + 12), *(source + 8), *(source + 4), *(source));

            // Load the 4 source int32's (also actually 4 int32s apart)
            second_4_int32s = _mm_set_epi32(*(reference + 12), *(reference + 8), *(reference + 4), *(reference));

            // Compare the int32's
            result = _mm_cmpeq_epi32(first_4_int32s, second_4_int32s);

            expected = method0 (result);
            result1 = method1 (result);
            result2 = method2 (result);
            result3 = method3 (result);
            if (result1 != expected) printf ("method1, index %d,%d expected %d, actual %d\n", index1, index2, expected, result1);
            if (result2 != expected) printf ("method2, index %d,%d expected %d, actual %d\n", index1, index2, expected, result2);
            if (result3 != expected) printf ("method3, index %d,%d expected %d, actual %d\n", index1, index2, expected, result3);
            }

    return 0;
    }

//----------------------------------------------------------------------------

【讨论】:

  • 无需遮罩。只需执行_mm_srl_epi32(value, 31) 即可将 0xFFFFFFFF 转换为 0x00000001。
  • @Raymond:谢谢,您的建议肯定更简单、更有效。我更新了代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
  • 2014-02-23
  • 2011-01-23
  • 1970-01-01
  • 2023-03-10
  • 2013-09-08
  • 2019-05-27
相关资源
最近更新 更多