2012-11-20 9 views
6

Tôi đang cố gắng trích xuất 4 byte ra khỏi thanh ghi 128 bit một cách hiệu quả. Vấn đề là mỗi giá trị nằm trong 32 bit {120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}. Tôi muốn biến đổi 128 bit thành 32 bit thành dạng {120,55,42,120}.Trích xuất SSE xáo trộn giá trị 32 bit chỉ với SSE2

Các "thô" mã trông giống như sau:

__m128i byte_result_vec={120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}; 
unsigned char * byte_result_array=(unsigned char*)&byte_result_vec; 
result_array[x]=byte_result_array[0]; 
result_array[x+1]=byte_result_array[4]; 
result_array[x+2]=byte_result_array[8]; 
result_array[x+3]=byte_result_array[12]; 

Mã của tôi SSSE3 là:

unsigned int * byte_result_array=...; 
__m128i byte_result_vec={120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}; 
const __m128i eight_bit_shuffle_mask=_mm_set_epi8(1,1,1,1,1,1,1,1,1,1,1,1,0,4,8,12);  
byte_result_vec=_mm_shuffle_epi8(byte_result_vec,eight_bit_shuffle_mask); 
unsigned int * byte_result_array=(unsigned int*)&byte_result_vec; 
result_array[x]=byte_result_array[0]; 

Làm thế nào tôi có thể làm một cách hiệu quả này với SSE2. Có phiên bản tốt hơn với SSSE3 hay SSE4 không?

Trả lời

9

Bạn có thể xem a previous answer of mine để biết một số giải pháp cho thao tác này và thao tác ngược lại.

Đặc biệt trong SSE2 bạn có thể làm điều đó bằng cách đầu tiên đóng gói các số nguyên 32-bit thành số nguyên 16-bit có chữ ký và bão hòa:

byte_result_vec = _mm_packs_epi32(byte_result_vec, byte_result_vec); 

Sau đó, chúng tôi đóng gói những giá trị 16-bit vào giá trị 8-bit unsigned sử dụng bão hòa unsigned:

byte_result_vec = _mm_packus_epi16(byte_result_vec, byte_result_vec); 

sau đó chúng tôi có thể cuối cùng mất giá trị của chúng tôi từ thấp 32-bit của thanh ghi:

int int_result = _mm_cvtsi128_si32(byte_result_vec); 
unsigned char* byte_result_array = (unsigned char*)&int_result; 
result_array[x] = byte_result_array[0]; 
result_array[x+1] = byte_result_array[1]; 
result_array[x+2] = byte_result_array[2]; 
result_array[x+3] = byte_result_array[3]; 

EDIT: Ở trên giả định rằng các từ 8 bit ban đầu trong các byte thấp của các từ 32 bit tương ứng và phần còn lại được lấp đầy với 0 s, vì nếu không chúng sẽ bị kẹp trong quá trình đóng gói bão hòa. Vì vậy, các hoạt động như sau:

   byte 15        0 
        0 0 0 D 0 0 0 C 0 0 0 B 0 0 0 A 

_mm_packs_epi32 -> 0 D 0 C 0 B 0 A 0 D 0 C 0 B 0 A 

_mm_packus_epi16 -> D C B A D C B A D C B A D C B A 
               ^^^^^^^ 

_mm_cvtsi128_si32 -> int DCBA, laid out in x86 memory as bytes A B C D 

-> reinterpreted as unsigned char array { A, B, C, D } 

Nếu byte uninterresting không đầy 0 s ban đầu, bạn cần phải che giấu chúng đi trước:

byte_result_vec = _mm_and_si128(byte_result_vec, _mm_set1_epi32(0x000000FF)); 

Hoặc nếu các byte interresting là bước đầu trong byte cao, bạn phải chuyển chúng thành các byte thấp trước:

byte_result_vec = _mm_srli_epi32(byte_result_vec, 24); 

Hoặc, nếu bạn thực sự muốn { D, C, B, A } (không tuân thủ rõ ràng là rõ ràng với tôi từ câu hỏi của bạn), sau đó, số tiền này chỉ chuyển đổi chỉ mục mảng trong các bài tập (hoặc luân phiên tạo ra một bản trộn 32 bit (_mm_shuffle_epi32) trên thanh ghi SSE ban đầu trước đó).

+0

đây là câu trả lời hoàn hảo. Làm thế nào tôi có thể upvote nó hai lần? :) điều này đã giúp tôi rất nhiều. Bạn có biết cách nào tốt hơn với SSE4 không? –

+0

@martins: với SSSE3 trở lên, bạn chỉ muốn có PSHUFB (đó là những gì mã hiện tại của bạn nên biên dịch). –

+0

@martins Tôi không giỏi thạo SSE> 2, có lẽ tôi sẽ cố gắng xem xét nó. –