#c #sse #simd #avx #avx2
#c #sse #simd #avx #avx2
Вопрос:
Предположим, у меня есть четыре __m128i
переменные, которые содержат данные, полученные в результате некоторых вычислений. Например, скажем:
__m128i a = _mm_set_epi64x(1, 11);
__m128i b = _mm_set_epi64x(2, 22);
__m128i c = _mm_set_epi64x(3, 33);
__m128i d = _mm_set_epi64x(4, 44);
Я хочу инициализировать две __m256i
переменные, где первая содержит все старшие 64 бита четырех переменных, а вторая содержит младшие 64 бита каждой. Итак, я хочу иметь:
__m256i x = ...; // x = { 4, 3, 2, 1 };
__m256i y = ...; // y = { 44, 33, 22, 11 };
Очевидный способ сделать это — использовать _mm256_set_epi64x
и _mm_extract_epi64
. Однако, это, вероятно, не особенно быстро. Есть ли более быстрый способ сделать это? В частности, для доступа к 64 старшим битам я не вижу подходящей команды load (в SSE2 есть загрузка для младших 64 бит) или shuffle (кажется, нет «64-битного перемешивания»).
Комментарии:
1. Какую инструкцию вы видели для загрузки младших 64 битов?
2. @JohnZwinck _mm_loadl_epi64 , но это для 128-разрядных регистров.
3. Вы упомянули, что нет 64-битного перемешивания, но кому оно может понадобиться? У нас есть _mm256_shuffle_epi32, и если мы хотим перетасовать 64-разрядные фрагменты, мы можем просто заявить, что мы делаем 32-разрядные фрагменты, и выполнить перетасовку таким образом, чтобы соседние 32-разрядные фрагменты перемещались вместе.
Ответ №1:
Если я правильно понял ваш вопрос, это простое преобразование 4×2 (или 2×4 transpose?).
Вот код, который работает для меня:
#include <iostream>
#include <immintrin.h>
using namespace std;
int main() {
__m128i a = _mm_set_epi64x(1, 11);
__m128i b = _mm_set_epi64x(2, 22);
__m128i c = _mm_set_epi64x(3, 33);
__m128i d = _mm_set_epi64x(4, 44);
__m256i ac = _mm256_castsi128_si256(a);
ac = _mm256_inserti128_si256(ac, c, 1); // {3, 33, 1, 11}
__m256i bd = _mm256_castsi128_si256(b);
bd = _mm256_inserti128_si256(bd, d, 1); // {4, 44, 2, 22}
__m256i high = _mm256_unpackhi_epi64(ac, bd);
__m256i low = _mm256_unpacklo_epi64(ac, bd);
uint64_t t[4];
_mm256_storeu_si256((__m256i*) t, high);
for (int i = 0; i < 4; i) {
cout << t[i] << endl;
}
_mm256_storeu_si256((__m256i*) t, low);
for (int i = 0; i < 4; i) {
cout << t[i] << endl;
}
return 0;
}
Это должно быть скомпилировано в 4 инструкции.
Комментарии:
1. Умный. Моя попытка выглядела как шесть инструкций.