2014-07-08 15 views
0

SSE2 내장 함수에서 처음으로 다이빙을하고 있는데 어떻게해야할지 모르겠다.SSE2 intrinsics - 2 개의 __m128i를 비교하여 4 개의 int32를 포함하여 각각 몇 개가 같은지 알아보기

4 개의 int32를 4 개의 다른 int32와 비교하여 얼마나 많은 수가 동일한 지 계산하고 싶습니다. 그래서 처음 4 개의 int32를 읽은 다음 __m128i로 설정하고 두 번째 세트에서 동일한 작업을 수행 한 다음 _mm_cmpeq_epi32를 사용하여 비교합니다.

이렇게하면 정수가 같은지 여부에 따라 4 개의 int32가 포함 된 __m128i가 만들어지며 각각은 0xffffffff 또는 0이됩니다.

그러나 그 결과 __m128i에서 까지 실제로 얻는 방법을 알 수는 없습니다.

누구나 올바른 방향으로 나를 가리킬 수 있습니까?

내가 그것을 함께 조립할거야까지 같이 코드 :

 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 ?!?! 
+1

이와 같이 수집 된로드를 수행하는 것은 성능이 나쁜 방법입니다. 인접한 데이터를 사용할 수없는 경우 스칼라 코드를 사용하는 것이 좋습니다. –

+1

네, 여기에서하는 일은 순차적으로하는 것보다 훨씬 느립니다. 따라서 메모리 액세스 패턴을 변경할 수 없다면 시간을 낭비하지 마십시오. – Mysticial

+0

당신은 단지 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 개의 합계를 유지할 수 있습니다. – Apriori

답변

0

당신과 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; 
    } 

//---------------------------------------------------------------------------- 
+0

마스크 할 필요가 없습니다. 단지'_mm_srl_epi32 (value, 31)'을 실행하여 0xFFFFFFFF를 0x00000001로 변환하십시오. –

+0

@ Raymond : 감사합니다. 귀하의 제안은 확실히 간단하고 효율적입니다. 코드를 업데이트했습니다. – ScottD