2012-04-02 4 views
3

이 질문은 이전에 답변 된 질문입니다. Fast 24-bit array -> 32-bit array conversion? 하나의 대답으로, interjay 친절하게도 RGB24 -> RGB32 변환을위한 SSE3 코드를 게시했지만, 나는 또한 역방향 변환 (RGB32 -> RGB24) . 나는 그것을 쏜 (아래 참조)과 내 코드는 확실히 작동하지만, 그것은 interjay의 코드보다 복잡하고, 눈에 띄게 느린 너무했습니다. 지침을 정확히 역전하는 방법을 알 수 없었습니다. _mm_alignr_epi8은이 경우 도움이되지 않지만 SSE3에 익숙하지 않았습니다. 비대칭 성은 피할 수 없습니까, 아니면 교대조와 ORing을 대신 할 수 있습니까?고속 32 비트 어레이 -> SSE3에서 24 비트 어레이 변환? (RGB32 -> RGB24)

RGB32 -> RGB24 :

__m128i *src = ... 
__m128i *dst = ... 
__m128i mask = _mm_setr_epi8(0,1,2,4, 5,6,8,9, 10,12,13,14, -1,-1,-1,-1); 
for (UINT i = 0; i < Pixels; i += 16) { 
    __m128i sa = _mm_shuffle_epi8(_mm_load_si128(src), mask); 
    __m128i sb = _mm_shuffle_epi8(_mm_load_si128(src + 1), mask); 
    __m128i sc = _mm_shuffle_epi8(_mm_load_si128(src + 2), mask); 
    __m128i sd = _mm_shuffle_epi8(_mm_load_si128(src + 3), mask); 
    _mm_store_si128(dst, _mm_or_si128(sa, _mm_slli_si128(sb, 12))); 
    _mm_store_si128(dst + 1, _mm_or_si128(_mm_srli_si128(sb, 4), _mm_slli_si128(sc, 8))); 
    _mm_store_si128(dst + 2, _mm_or_si128(_mm_srli_si128(sc, 8), _mm_slli_si128(sd, 4))); 
    src += 4; 
    dst += 3; 
} 

RGB24 -> RGB32 (의례 interjay는) :

__m128i *src = ... 
__m128i *dst = ... 
__m128i mask = _mm_setr_epi8(0,1,2,-1, 3,4,5,-1, 6,7,8,-1, 9,10,11,-1); 
for (UINT i = 0; i < Pixels; i += 16) { 
    __m128i sa = _mm_load_si128(src); 
    __m128i sb = _mm_load_si128(src + 1); 
    __m128i sc = _mm_load_si128(src + 2); 
    __m128i val = _mm_shuffle_epi8(sa, mask); 
    _mm_store_si128(dst, val); 
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sb, sa, 12), mask); 
    _mm_store_si128(dst + 1, val); 
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sb, 8), mask); 
    _mm_store_si128(dst + 2, val); 
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sc, 4), mask); 
    _mm_store_si128(dst + 3, val); 
    src += 3; 
    dst += 4; 
} 
+0

그래서 SSE4.1은 허용되지 않습니까? – harold

+0

4 개의 입력 레지스터에서 6 개의 마스크를 사용하여 3 개의 출력 레지스터로 변환하면됩니다. 'pshufb'는 1 바이트를 0으로 설정하거나 마스크로 색인 된 값으로 설정하기 때문에 3 개 또는 3 개를 사용할 수 없습니다. – hirschhornsalz

답변

0

당신은 this answer을 가지고 RGB24를 RGB32로 이동합니다 셔플 마스크를 변경할 수 있습니다.

큰 차이점은 셔플을 직접 계산하고 이동을 피하기 위해 비트 단위 연산을 사용하는 것입니다. 또한 정렬 된 쓰기 대신 정렬 된 스트리밍 쓰기를 사용하면 캐시가 오염되지 않습니다.

0

오래된 질문,하지만 난 그렇게 같은 문제를 해결하기 위해 노력했다 ...

당신이 두 번째 피연산자를 오른쪽으로 정렬하는 경우가 palignr를 사용할 수있는, 즉 낮은 바이트에 0을 넣어. 두 번째, 세 번째 및 네 번째 단어의 왼쪽 맞춤 버전과 첫 번째, 두 번째 및 세 번째 단어의 오른쪽 맞춤 버전이 필요합니다.

두 번째와 세 번째 단어의 경우, GCC는 왼쪽 정렬 된 것에서 오른쪽 정렬 된 버전을 계산하기 위해 쉬프트를 사용하면 약간 더 행복합니다. 두 개의 서로 다른 pshufb를 사용하면 3 개의 불필요한 움직임이 발생합니다.

다음은 코드입니다. 정확히 8 개의 레지스터를 사용합니다. 64 비트 모드를 사용 중이라면 2로 풀어보십시오.

__m128i mask_right = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, 0x80, 0x80, 0x80, 0x80); 
    __m128i mask = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0); 

    for (; n; n -= 16, d += 48, s += 64) { 
      __m128i v0 = _mm_load_si128((__m128i *) &s[0]); 
      __m128i v1 = _mm_load_si128((__m128i *) &s[16]); 
      __m128i v2 = _mm_load_si128((__m128i *) &s[32]); 
      __m128i v3 = _mm_load_si128((__m128i *) &s[48]); 

      v0 = _mm_shuffle_epi8(v0, mask_right); 
      v1 = _mm_shuffle_epi8(v1, mask); 
      v2 = _mm_shuffle_epi8(v2, mask); 
      v3 = _mm_shuffle_epi8(v3, mask); 

      v0 = _mm_alignr_epi8(v1, v0, 4); 
      v1 = _mm_slli_si128(v1, 4);  // mask -> mask_right 
      v1 = _mm_alignr_epi8(v2, v1, 8); 
      v2 = _mm_slli_si128(v2, 4);  // mask -> mask_right 
      v2 = _mm_alignr_epi8(v3, v2, 12); 

      _mm_store_si128((__m128i *) &d[0], v0); 
      _mm_store_si128((__m128i *) &d[16], v1); 
      _mm_store_si128((__m128i *) &d[32], v2); 
    } 

중앙 부분도 이와 같이 작성할 수 있습니다. 컴파일러는 하나의 명령어를 더 적게 생성하지만 병렬 처리가 조금 더 복잡해 보이지만 올바른 대답을 제공하려면 벤치마킹이 필요합니다.

  v0 = _mm_shuffle_epi8(v0, mask_right); 
      v1 = _mm_shuffle_epi8(v1, mask); 
      v2 = _mm_shuffle_epi8(v2, mask_right); 
      v3 = _mm_shuffle_epi8(v3, mask); 

      __m128i v2l = v2; 
      v0 = _mm_alignr_epi8(v1, v0, 4); 
      v1 = _mm_slli_si128(v1, 4);    // mask -> mask_right 
      v2 = _mm_alignr_epi8(v3, v2, 12); 
      v2l = _mm_srli_si128(v2l, 4);   // mask_right -> mask 
      v1 = _mm_alignr_epi8(v2l, v1, 8);