#vectorization #simd #intrinsics #avx2
#векторизация #simd #встроенные #avx2
Вопрос:
У меня есть регистр __m256i, и я хочу извлечь 4 младших 32 бита из каждой 64-битной группы, упаковать их и сохранить непрерывно в памяти. Т.е., если регистр __m256i содержит 8 32-битных слов: { a0, a1, a2, a3, a4, a5, a6, a7 }, я хочу сохранить в памяти последовательно четыре слова { a0, a2, a4, a6 }
Я придумал следующий код:
void mystore(uint32 *dst, const __m256iamp; src)
{
__m256 ps256 = _mm256_castsi256_ps(src);
__m128 lo128 = _mm256_extractf128_ps(ps256, 0);
__m128 hi128 = _mm256_extractf128_ps(ps256, 1);
__m128 pack128 = _mm_shuffle_ps(lo128, hi128, 0 (2<<2) (0<<4) (2<<6));
__m128i r = _mm_castps_si128(pack);
_mm256_storeu_si256( reinterpret_cast<__m128i*>(dst), r )
}
Если я прав, операции приведения используются только для проверки типов компилятора, но фактически они эквивалентны безоперационному режиму, поэтому общая стоимость задержки составляет 3 для инструкции перемешивания и 2 инструкций извлечения, плюс стоимость невыровненного хранилища.
Есть ли более быстрый способ сделать это?
Спасибо
Комментарии:
1.
_mm256_extractf128_ps(ps256, 0);
это также просто приведение. Нижняя половина каждого регистра YMM доступна как соответствующий регистр XMM, и, к счастью, компиляторы знают это и не наказывают нас за запись extract(v, 0) вместо того, что более прямо выражает правильная_mm_cast
внутренняя функция оптимального asm. (Аналогично, компиляторы используют MOVD при записи_mm_extract_epi32(v, 0)
вместо фактического PEXTRD).2. Интересно, я этого не знал.
Ответ №1:
Вы можете попробовать использовать что-то вроде этого:
const __m256i K_PERM = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7);
inline void mystore(uint32_t * dst, const __m256i amp; src)
{
__m256i permuted = _mm256_permutevar8x32_epi32(src, K_PERM);
__m128i lo128 = _mm256_extractf128_si256(permuted, 0);
_mm_storeu_si128((__m128i*)dst, lo128);
}
Комментарии:
1. Учитывая, что команда перестановки имеет задержку 3, исходя из приведенных выше комментариев, это кажется более дорогостоящим, чем существующее решение. Тем не менее, я попробую.
2. Я думаю, что основным ограничением является пропускная способность памяти (для обоих вариантов).
3. Не используйте глобальные
__m256i
константы; компиляторы инициализируют их неэффективно (путем копирования в BSS во время выполнения). В противном случае да, одинvpermd
— это то, что вы хотите. (Вы могли бы использовать_mm256_castsi256_si128
вместо извлечения, но большинство компиляторов оптимизируют извлечение (v, 0). ) Поскольку вас интересует только младший 128 результата перемешивания, вы могли бы использовать только__m128i
элемент управления перемешиванием, поэтому вам не нужно загружать столько постоянных данных. (_mm256_castsi128_si256
чтобы использовать его с 256-битным перемешиванием.)