2015-09-11 5 views
6

В AVX есть только 128 бит PSHUFBAVX2 VPSHUFB эмуляция AVX

VPSHUFB xmm1, xmm2, xmm3/m128 

и только AVX2 имеет полную PSHUFB для целых 256 бит AVX регистрирует

VPSHUFB ymm1, ymm2, ymm3/m256 

Как эта инструкция может эмулировать эффективно с встроенными функциями AVX?

Также в этом конкретном случае источник содержит только 8 элементов (байтов), но их можно было перемещать в пределах 32 байт адресата. Таким образом, это не проблема для запуска всего 2 x PSHUFB.

Проблема, которую я нахожу с помощью VPSHUFB, относится к 16 (0x10) как 0, только 128 и вверх заполнены нулем! (старший бит) Возможно ли это сделать без добавления сравнений и маскировки?

+4

Возможно, вы не заметили, но AVX2 'VPSHUFB ymm, ymm, ymm/m256' на самом деле не является 256-битным перетасовкой, а это 2x 128-бит shuffle. –

+0

Интересно. Благодаря! – alecco

ответ

7

Как заметил @MaratDukhan, _mm256_shuffle_epi8 (т. Е. VPSHUFB для ymm-s) не выполняет полный 32-байтовый случайный перетасовка. Что касается меня, это довольно жалко ...

Вот почему для того, чтобы эмулировать без AVX2 вы можете просто разделить каждый регистр на две половины, каждая половина переставлять, а затем объединить вместе:

//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; 
} 

Если вы действительно хотите полностью перетасовать 32-байтовый регистр, вы можете следить за приближением от this paper. Смешайте каждую половину с каждой половиной, затем смешайте результаты вместе. Без AVX2 было бы что-то подобное:

//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; 
} 

Если вы точно знаете, что используется только нижняя половина reg, то вы можете удалить линии для reg1, res10, res11 и удалить сравнение и смешивание. В самом деле, может быть более эффективным придерживаться SSE и использовать 128-битные регистры, если у вас нет AVX2.

Общие 32-байтовые перетасовки может быть существенно оптимизирован с 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; 
} 

Остерегайтесь: Код не проверяли!

+0

Довольно тщательный ответ, намного больше, чем я ожидал. Большое спасибо! – alecco

+0

Знаете ли вы, что ваша реализация только AVX содержит '_mm256_extracti128_si256', который доступен только в AVX2? – plasmacel

+0

@stgatilov Проверьте это снова, '_mm256_extracti128_si256' компилируется в' vextracti128', что является AVX2. Вы говорите о '_mm256_extractf128_si256', который компилируется в' vextractf128'. Они похожи, но в то время как 'vextractf128' работает с доменом с плавающей точкой,' vextracti128' работает с целым доменом. – plasmacel