2017-08-24 17 views
5

Có cách nội tại hay cách hiệu quả khác để đóng gói lại thành phần 32 bit cao/thấp của các thành phần 64-bit của thanh ghi AVX vào thanh ghi SSE không? Một giải pháp sử dụng AVX2 là ok.Cách hiệu quả (trên Ryzen) để trích xuất các phần tử lẻ của __m256 thành __m128?

Cho đến nay tôi đang sử dụng đoạn mã sau, nhưng profiler nói nó chậm trên Ryzen 1800X:

// Global constant 
const __m256i gHigh32Permute = _mm256_set_epi32(0, 0, 0, 0, 7, 5, 3, 1); 

// ... 

// function code 
__m256i x = /* computed here */; 
const __m128i high32 = _mm256_castsi256_si128(_mm256_permutevar8x32_epi32(x), 
    gHigh32Permute); // This seems to take 3 cycles 
+1

Vì vậy, bạn muốn trích xuất các phần tử 32 bit lẻ hoặc được đánh số? ví dụ như AVX512 '_mm256_cvtepi64_epi32' (' vpmovqd')? Tôi không nghĩ rằng bạn sẽ đánh bại 1 lệnh shuffle với độ trễ 3 chu kỳ, bởi vì các nút xoay ngang làn đường luôn có độ trễ 3c trên CPU Intel. Giải pháp 'vpermd' của bạn có thông lượng chu trình đơn. –

+0

Nếu bạn cần nó nhanh hơn, bạn sẽ phải làm cho mã xung quanh sử dụng nó ít hơn, hoặc không yêu cầu vượt qua làn đường hoặc một cái gì đó! Hoặc có thể bằng cách nào đó đóng gói hai nguồn vào một kết quả 256b với 'shufps' (ngoại trừ nó không phải là làn đường giao nhau để nó không giải quyết được vấn đề của bạn, và không có lệnh' vpackqd' và hướng dẫn đóng gói nào cũng không bị vượt qua.) –

+0

@PeterCordes, vâng, tôi muốn trích xuất các phần tử 32 bit lẻ hoặc được đánh số từ một thanh ghi 256 bit sang thanh ghi 128 bit. Cảm ơn bạn đã tham khảo AVX512! Tôi không có nó trên Ryzen 1800X, nhưng mong muốn di chuyển đến nó một lần ... Những phần tử 32 bit này là phần cao và thấp của đôi 64-bit, vì vậy tôi không thấy một cách để thay đổi mã xung quanh . –

Trả lời

3

On Intel, mã của bạn sẽ được tối ưu. Một hướng dẫn 1-uop là tốt nhất bạn sẽ nhận được. (Ngoại trừ bạn có thể muốn sử dụng vpermps để tránh bất kỳ rủi ro nào cho sự chậm trễ bỏ qua int/FP, nếu vectơ đầu vào của bạn được tạo bởi lệnh pd thay vì tải hoặc một thứ gì đó. thường là tốt trên Intel, nhưng tôi ít chắc chắn về việc cho ăn kết quả của một lệnh FP để trộn ngẫu nhiên.)

Mặc dù nếu điều chỉnh cho Intel, bạn có thể thử thay đổi mã xung quanh để bạn có thể trộn vào đáy 64 -bộ phận của mỗi làn 128b, để tránh sử dụng trộn ngẫu nhiên làn đường. (Sau đó, bạn chỉ có thể sử dụng vshufps ymm, hoặc nếu điều chỉnh cho KNL, vpermilps từ 2-input vshufps là chậm hơn.)

Với AVX512, có _mm256_cvtepi64_epi32 (vpmovqd) mà gói các yếu tố trên làn xe, với cắt ngắn.


On Ryzen, shuffles làn vượt chậm. Agner Fog không có số cho vpermd, nhưng anh ta liệt kê vpermps (có thể sử dụng cùng phần cứng nội bộ) ở 3 uops, 5c độ trễ, một cho mỗi thông lượng 4c.

vextractf128 xmm, ymm, 1 rất hiệu quả trên Ryzen (độ trễ 1c, thông lượng 0.33c), không đáng ngạc nhiên vì nó theo dõi thanh ghi 256b là hai nửa 128b. shufps cũng hiệu quả (độ trễ 1c, thông lượng 0.5c) và sẽ cho phép bạn trộn hai thanh ghi 128b vào kết quả bạn muốn.

Điều này cũng giúp bạn tiết kiệm 2 thanh ghi cho các mặt nạ trộn 2 vpermps bạn không cần nữa.

Vì vậy, tôi muốn đề nghị:

__m256d x = /* computed here */; 

// Tuned for Ryzen. Sub-optimal on Intel 
__m128 hi = _mm_castpd_ps(_mm256_extractf128_pd(x, 1)); 
__m128 lo = _mm_castpd_ps(_mm256_castpd256_pd128(x)); 
__m128 odd = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(3,1,3,1)); 
__m128 even = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(2,0,2,0)); 

On Intel, sử dụng 3 shuffles thay vì 2 cung cấp cho bạn 2/3rds của thông lượng tối ưu, với độ trễ 1c thêm cho kết quả đầu tiên.

+0

tôi đã đo rằng '__m128i const high32 = _mm256_castsi256_si128 (_mm256_permutevar8x32_epi32 (_mm256_castpd_si256 (x), gHigh32Permute));' là nhanh hơn 'const __m128i high32 = _mm_castps_si128 (_mm256_castps256_ps128 (_mm256_permutevar8x32_ps (_mm256_castpd_ps (x), gHigh32Permute)));' . Vì vậy, có lẽ cũng có một hình phạt cho 'double' to' float' bypass? –

+0

@SergeRogatch: Không có khả năng bị xáo trộn. Nhiều khả năng, 'vpermd' chỉ hoạt động khác với' vpermps'. (Agner không liệt kê cả hai nên tôi phải đoán). Hoặc rằng bất cứ điều gì bạn đang tiêu thụ kết quả với không tốt hơn khi nó đến từ một shuffle số nguyên? Tuy nhiên, AMD đã có sự khác biệt so với các chỉ số toán học FP thực tế so với các chỉ số toán học FP thực tế, theo Agner. (Hầu như lúc nào cũng không liên quan, nhưng đó là một đầu mối về việc thực hiện nội bộ, như có thể có một số bit thẻ phụ được lưu trữ với một vectơ.) –

+0

Không nên 'hi' và' lo' được đổi chỗ trong '__m128 odd = _mm_shuffle_ps (hi , lo, _MM_SHUFFLE (3,1,3,1)); '? –

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