2017-08-24 17 views
5

Czy istnieje wewnętrzny lub inny skuteczny sposób na przepakowanie 32/bitowych składników 64-bitowych rejestru AVX do rejestru SSE? Rozwiązanie wykorzystujące AVX2 jest w porządku.Wydajny (na Ryzen) sposób na wyodrębnienie nieparzystych elementów __m256 w __m128?

tej pory używam następujący kod, ale mówi, że to profiler slow na 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

Czy chcesz wyodrębnić elementy 32-bitowe nieparzyste lub parzyste? tj. jak AVX512 '_mm256_cvtepi64_epi32' (' vpmovqd')? Nie sądzę, że uda ci się pokonać 1 instrukcję shuffle z 3-cyklowym opóźnieniem, ponieważ tasowanie podczas przekraczania linii zawsze ma opóźnienie 3c na procesorach Intela. Twoje rozwiązanie 'vpermd' ma przepustowość jednokrokową. –

+0

Jeśli potrzebujesz, aby był szybszy, będziesz musiał sprawić, że otaczający kod będzie go używał mniej lub nie będzie wymagał przejścia linii czy czegoś! A może w jakiś sposób spakuj dwa źródła do wyniku 256b z 'shufps' (oprócz tego, że nie jest to przejście przez pas, więc to nie rozwiązuje twojego problemu, i nie ma instrukcji' vpackqd' i instrukcje pakowania również nie przepuszczają pasa.) –

+0

@PeterCordes, tak, chcę wyodrębnić nieparzyste lub parzyste 32-bitowe elementy z 256-bitowego rejestru do 128-bitowego rejestru. Dzięki za odniesienie do AVX512! Nie mam go na Ryzen 1800X, ale czekam na migrację do niego raz ... Te 32-bitowe elementy są wysokimi i niskimi częściami 64-bitowych podwójnych, więc nie widzę sposobu na zmianę otaczającego kodu . –

Odpowiedz

3

Intel, Twój kod będzie optymalna. Jedna instrukcja 1-uop jest najlepsza, jaką otrzymasz. (Z wyjątkiem, możesz chcieć użyć vpermps, aby uniknąć ryzyka opóźnienia bypassu int/FP, jeśli twój wektor wejściowy został utworzony przez instrukcję pd zamiast obciążenia lub czegoś.) Używanie wyniku shuffle FP jako wejścia do instrukcji całkowitych jest Zwykle dobrze na Intela, ale jestem mniej pewny co do dostarczania wyniku instrukcji FP do shuffle w liczbach całkowitych.)

Chociaż po dostrojeniu do Intela, możesz spróbować zmienić otaczający kod, aby można było przetasować do dołu 64 -błydy każdej linii 128b, aby uniknąć użycia tasowania do przekraczania linii. (Potem można po prostu użyć vshufps ymm, czy strojenie KNL, vpermilps od 2-Input vshufps jest wolniejszy).

Z AVX512, tam _mm256_cvtepi64_epi32 (vpmovqd) który pakuje elementów w pasach, z obcięcia.


Na Ryzen, tasuje Lane-crossing są powolne. Agner Fog nie ma numerów dla vpermd, ale wymienia vpermps (która prawdopodobnie używa tego samego sprzętu wewnętrznie) w 3 uops, 5c opóźnienia, jeden na przepustowość 4c.

vextractf128 xmm, ymm, 1 jest bardzo wydajny na Ryzen (1k opóźnienia, 0.33c przepustowości), nic dziwnego, ponieważ śledzi już rejestry 256b jako dwie połówki 128b. shufps jest również wydajny (opóźnienie 1c, przepustowość 0.5c) i pozwoli ci przetasować dwa rejestry 128b do pożądanego wyniku.

To również pozwala na zapisanie 2 rejestrów dla masek shuffle 2 vpermps, których już nie potrzebujesz.

Więc sugeruję:

__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)); 

Intel, przy użyciu 3 przetasowań zamiast 2 daje 2/3rds optymalnej przepustowości, z 1c dodatkowego opóźnienia dla pierwszego wyniku.

+0

ja zmierzono, że 'const __m128i high32 = _mm256_castsi256_si128 (_mm256_permutevar8x32_epi32 (_mm256_castpd_si256 (x) gHigh32Permute));' jest większa niż 'const __m128i high32 = _mm_castps_si128 (_mm256_castps256_ps128 (_mm256_permutevar8x32_ps (_mm256_castpd_ps (x) gHigh32Permute)));' . Być może jest też kara za "podwójne" przejście na "float"? –

+0

@SergeRogatch: mało prawdopodobne w przypadku tasowania. Bardziej prawdopodobne jest, że 'vpermd' po prostu działa inaczej niż' vpermps'. (Agner nie wymienił ich obu, więc musiałem zgadywać). Czy to, z czym konsumujesz wynik, jest lepsze, gdy pochodzi z shuffle w liczbach całkowitych? Jednak według Agnera, AMD ma zmienne różnice w stosunku do podwójnych różnic dla rzeczywistych instrukcji matematycznych FP. (Prawie zawsze nieistotne oczywiście, ale jest to wskazówka dotycząca wewnętrznej implementacji, jak być może jest kilka dodatkowych bitów znaczników przechowywanych z wektorem). –

+0

Nie należy zamieniać 'hi' i' lo' w '__m128 odd = _mmmmshshle_ps (hi , lo, _MM_SHUFFLE (3,1,3,1)); '? –

Powiązane problemy