Инициализировать __m256i из 64 старших или младших бит четырех переменных __m128i

Предположим, у меня есть четыре __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 старшим битам я не вижу подходящей нагрузки (есть загрузка для младших 64 бит в SSE2) или инструкции перемешивания (похоже, нет «64-битного перемешивания»).


person Gideon    schedule 06.07.2014    source источник
comment
Какую инструкцию вы видели для загрузки младших 64 бита?   -  person John Zwinck    schedule 06.07.2014
comment
@JohnZwinck _ mm_loadl_epi64, но это для 128-битных регистров.   -  person Gideon    schedule 06.07.2014
comment
Вы упомянули, что 64-битного перемешивания не существует, но кому он может понадобиться? У нас есть _mm256_shuffle_epi32, и если мы хотим перемешать 64-битные фрагменты, мы можем просто заявить, что мы делаем 32-битные фрагменты, и выполнить перемешивание таким образом, чтобы смежные 32-битные фрагменты перемещались вместе.   -  person John Zwinck    schedule 06.07.2014


Ответы (1)


Если я правильно понял ваш вопрос, это простое транспонирование 4x2 (или транспонирование 2x4?).

Вот код, который у меня работает:

#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 инструкции.

person Norbert P.    schedule 06.07.2014
comment
Умный. Моя попытка выглядела как шесть инструкций. - person John Zwinck; 06.07.2014