2015-09-11 19 views
6

Trong AVX chỉ có 128 bit PSHUFBAVX2 VPSHUFB thi đua trong AVX

VPSHUFB xmm1, xmm2, xmm3/m128 

và chỉ AVX2 có đầy đủ các PSHUFB cho toàn bộ 256 bit AVX đăng ký

VPSHUFB ymm1, ymm2, ymm3/m256 

Làm thế nào hướng dẫn này có thể được mô phỏng hiệu quả với nội tại AVX?

Cũng trong trường hợp cụ thể này, nguồn chỉ có 8 phần tử (byte) nhưng có thể di chuyển trong phạm vi 32 byte đầy đủ của đích đến. Vì vậy, không có vấn đề gì khi chạy chỉ 2 x PSHUFB.

Một vấn đề tôi đang tìm kiếm với VPSHUFB là nó xử lý 16 (0x10) là 0, chỉ 128 và lên được lấp đầy bằng không! (bộ bit cao nhất) Có thể làm điều đó mà không cần thêm so sánh và che giấu không?

+4

Có thể bạn đã không nhận thấy, nhưng AVX2 'VPSHUFB ymm, ymm, ymm/m256' không thực sự là một phát ngẫu nhiên 256 bit, thay vào đó là xáo trộn 128 bit. –

+0

Thú vị. Cảm ơn! – alecco

Trả lời

7

Như @MaratDukhan đã nhận thấy, _mm256_shuffle_epi8 (ví dụ: VPSHUFB đối với ymm-s) không thực hiện trộn hoàn toàn 32 byte. Đối với tôi, nó là khá một điều đáng tiếc ...

Đó là lý do để bắt chước nó mà không AVX2 bạn chỉ có thể chia nhỏ từng đăng ký thành hai nửa, mỗi nửa hoán, sau đó kết hợp với nhau:

//AVX only 
__m256i _emu_mm256_shuffle_epi8(__m256i reg, __m256i shuf) { 
    __m128i reg0 = _mm256_castsi256_si128(reg); 
    __m128i reg1 = _mm256_extractf128_si256(reg, 1); 
    __m128i shuf0 = _mm256_castsi256_si128(shuf); 
    __m128i shuf1 = _mm256_extractf128_si256(shuf, 1); 
    __m128i res0 = _mm_shuffle_epi8(reg0, shuf0); 
    __m128i res1 = _mm_shuffle_epi8(reg1, shuf1); 
    __m256i res = _mm256_setr_m128i(res0, res1); 
    return res; 
} 

Nếu bạn thực sự muốn trộn hoàn toàn thanh ghi 32 byte, bạn có thể làm theo cách tiếp cận từ this paper. Trộn từng nửa với mỗi nửa, sau đó trộn kết quả lại với nhau. Nếu không có AVX2 nó sẽ là một cái gì đó như thế:

//AVX only 
__m256i _emu_mm256_shuffle32_epi8(__m256i reg, __m256i shuf) { 
    __m128i reg0 = _mm256_castsi256_si128(reg); 
    __m128i reg1 = _mm256_extractf128_si256(reg, 1); 
    __m128i shuf0 = _mm256_castsi256_si128(shuf); 
    __m128i shuf1 = _mm256_extractf128_si256(shuf, 1); 
    __m128i res00 = _mm_shuffle_epi8(reg0, shuf0); 
    __m128i res01 = _mm_shuffle_epi8(reg0, shuf1); 
    __m128i res10 = _mm_shuffle_epi8(reg1, shuf0); 
    __m128i res11 = _mm_shuffle_epi8(reg1, shuf1); 
    __m128i res0 = _mm_blendv_epi8(res10, res00, _mm_cmplt_epi8(shuf0, _mm_set1_epi8(16))); 
    __m128i res1 = _mm_blendv_epi8(res11, res01, _mm_cmplt_epi8(shuf1, _mm_set1_epi8(16))); 
    __m256i res = _mm256_setr_m128i(res0, res1); 
    return res; 
} 

Nếu bạn biết chắc chắn rằng chỉ có nửa dưới của reg được sử dụng, sau đó bạn có thể loại bỏ các nếp cho reg1, res10, res11, và loại bỏ sự so sánh và trộn. Thật vậy, nó có thể hiệu quả hơn để gắn bó với SSE và sử dụng thanh ghi 128-bit nếu bạn không có AVX2.

Tổng 32-byte xáo trộn có thể được tối ưu hóa đáng kể với AVX2:

//Uses AVX2 
__m256i _ext_mm256_shuffle32_epi8(__m256i reg, __m256i shuf) { 
    __m256i regAll0 = _mm256_permute2x128_si256(reg, reg, 0x00); 
    __m256i regAll1 = _mm256_permute2x128_si256(reg, reg, 0x11); 
    __m256i resR0 = _mm256_shuffle_epi8(regAll0, shuf); 
    __m256i resR1 = _mm256_shuffle_epi8(regAll1, shuf); 
    __m256i res = _mm256_blendv_epi8(resR1, resR0, _mm256_cmpgt_epi8(_mm256_set1_epi8(16), shuf)); 
    return res; 
} 

Hãy coi chừng: Mã chưa được thử nghiệm!

+0

Khá một câu trả lời kỹ lưỡng, nhiều hơn tôi mong đợi. Cảm ơn rất nhiều! – alecco

+0

Bạn có biết rằng việc triển khai chỉ AVX của bạn có chứa '_mm256_extracti128_si256', chỉ khả dụng trong AVX2 không? – plasmacel

+0

@stgatilov Kiểm tra lại, '_mm256_extracti128_si256' biên dịch thành' vextracti128', là AVX2. Bạn đang nói về '_mm256_extractf128_si256', biên dịch thành' vextractf128'. Chúng giống nhau, nhưng trong khi 'vextractf128' hoạt động trên miền dấu phẩy động,' vextracti128' hoạt động trên tên miền nguyên. – plasmacel

Các vấn đề liên quan