Установка __m256i на значение двух значений __m128i

#gcc #intrinsics #avx

#c #sse #simd #avx

Вопрос:

Итак, в AVX есть функция от immintrin.h , которая должна позволять сохранять объединение двух __m128i значений в одно __m256i значение. Функция является

 __m256i _mm256_set_m128i (__m128i hi, __m128i lo)
  

Однако, когда я использую его, вот так:

 __m256i as[2]; __m128i s[4]; as[0] = _mm256_setr_m128i(s[0], s[1]);
  

Я получаю ошибку компиляции:

 error: incompatible types when assigning to type ‘__m256i’ from type ‘int 

Я действительно не понимаю, почему это происходит. Любая помощь приветствуется!

Комментарии:

1. Вы уверены, что эта функция находится в этом заголовке? Когда я пытаюсь использовать функцию, как у вас после #include <immintrin.h> , я получаю предупреждающее сообщение «неявное объявление _mm256_set_m128i «. В C неявное объявление означает, что компилятор предполагает, что функция вернет int , что также объясняет ошибку компилятора.

Ответ №1:

Похоже, что не все компиляторы _mm256_setr_m128i или даже _mm256_set_m128i не определены в immintrin.h . Поэтому я обычно просто определяю макросы по мере необходимости, заключая в квадратные скобки подходящие #ifdef s, которые проверяют компилятор и версию:

 #define _mm256_set_m128i(v0, v1)  _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1)

#define _mm256_setr_m128i(v0, v1) _mm256_set_m128i((v1), (v0))
  
  • В Intel ICC 11.1 и более поздних версиях есть оба _mm256_set_m128i и _mm256_setr_m128i .

  • MSVC 2012 и более поздние версии имеют только _mm256_set_m128i .

  • похоже, что у gcc / clang тоже нет, хотя я не проверял последние версии, чтобы узнать, было ли это исправлено.

Комментарии:

1. GCC, похоже, не хватает многих встроенных функций «composite». Недавно я обнаружил, что _mm256_loadu2_m128 и family — лучший способ выполнить транспонирование из памяти, поскольку (в ICC) он компилируется в две инструкции, которые не используют порт векторной перетасовки. В GCC встроенная сборка — единственный способ получить тот же эффект. Для AVX512 в GCC отсутствуют все встроенные функции сокращения.

2. @Mysticial: хм, хорошо, я понимаю проблему, о которой ты говоришь. Вы должны убедиться, что все ваши ссылки на память проходят через loadu , иначе компилятор может выполнить их с помощью movaps at -O0 . Решение: используйте gcc 4.9.2 или новее, тогда loadu произойдет свертка в vinsertf128. Или для gcc 4.8: всегда используйте по крайней мере -Og : «оптимизировать для отладки» и сохраняйте свой код, который будет давать сбой при компиляции в -O0 . goo.gl/eBPD5a . Обратите внимание, что clang имеет обе рассматриваемые встроенные функции, _mm256_set_m128i и _mm256_loadu2_m128 .

3. @PeterCordes О, это здорово! Я действительно тестировал с GCC 4.8. Спасибо!

4.Я только что столкнулся с этим. Я хотел vbroadcastf128 ввести константу pshufb в обе строки регистра ymm. Но gcc6.3.1 ужасен во всем, что я пробовал. Ваш макрос — лучшее из плохой ситуации, он компилируется в vmovdqa vinserti128 . (clang компилируется в вектор 256b). Другие попытки с gcc: const __m128 tmp = _mm_castsi128_ps(shufmask128); _mm256_castps_si256(_mm256_broadcast_ps(amp;tmp)) выполняется компиляция с загрузкой константы FP 128b, сохранения FP 128b в стек, а затем vinsertf128 из только что сохраненной копии. Приведение указателя было еще хуже, если вы можете в это поверить.

5. Боль, боль, еще раз боль. Это в gcc 8.1. Этого нет в gcc 7.3. Смотрите исходный код на github.com/gcc-mirror/gcc/blob/master/gcc/config/i386 /… . _mm256_set_m128i находится там.

Ответ №2:

У нас была такая же проблема, и мы использовали макрос, чтобы обойти ее.

 #ifdef __GNUC__ 
#if __GNUC__ < 8
#define _mm256_set_m128i(xmm1, xmm2) _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1), _mm256_castsi128_si256(xmm2), 2)
#define _mm256_set_m128f(xmm1, xmm2) _mm256_permute2f128_ps(_mm256_castps128_ps256(xmm1), _mm256_castps128_ps256(xmm2), 2)
#endif
#endif
  

Комментарии:

1. _mm256_insertf128_si256 всегда по крайней мере так же дешево, как _mm256_permute2f128_ps , или, по крайней мере, соответствующие инструкции asm. Я не проверял, как это компилируется для констант по сравнению с переменными времени выполнения.