Эффективный (на Ryzen) способ извлечения нечетных элементов __m256 в __m128?

Есть ли внутренний или другой эффективный способ переупаковки 32-разрядных компонентов с высоким / низким разрядом 64-разрядных компонентов регистра AVX в регистр SSE? Решение с использованием AVX2 в порядке.

Пока я использую следующий код, но профилировщик говорит, что он медленный на Ryzen 1800X:

// Global constant
const __m256i gHigh32Permute = _mm256_set_epi32(0, 0, 0, 0, 7, 5, 3, 1);

// ...

// function code
__m256i x = /* computed here */;
const __m128i high32 = _mm256_castsi256_si128(_mm256_permutevar8x32_epi32(x),
  gHigh32Permute); // This seems to take 3 cycles

person Serge Rogatch    schedule 24.08.2017    source источник
comment
Итак, вы хотите извлечь 32-битные элементы с нечетными или четными номерами? т.е. как AVX512 _mm256_cvtepi64_epi32 (vpmovqd)? Я не думаю, что вы собираетесь превзойти 1 инструкцию перемешивания с задержкой в ​​3 цикла, потому что перемешивание при пересечении полосы движения всегда имеет задержку 3c на процессорах Intel. Ваше vpermd решение имеет пропускную способность за один цикл.   -  person Peter Cordes    schedule 24.08.2017
comment
Если вам нужно, чтобы это было быстрее, вам придется заставить окружающий код использовать его меньше или не требовать пересечения полосы движения или что-то в этом роде! Или, может быть, как-то упаковать два источника в результат 256b с shufps (за исключением того, что это не пересечение полосы движения, поэтому это не решает вашу проблему, и нет инструкции vpackqd, и инструкции упаковки также не пересекают полосу).   -  person Peter Cordes    schedule 24.08.2017
comment
@PeterCordes, да, я хочу извлечь 32-битные элементы с нечетными или четными номерами из 256-битного регистра в 128-битный регистр. Спасибо за ссылку на AVX512! У меня его нет на Ryzen 1800X, но я с нетерпением жду возможности перейти на него один раз ... Эти 32-битные элементы являются высокими и низкими частями 64-битных двойников, поэтому я не вижу способа изменить окружающий код .   -  person Serge Rogatch    schedule 24.08.2017
comment
Хорошо, они должны быть в __m128i, или вы можете использовать перетасовку внутренней полосы, чтобы поместить нижнюю и верхнюю половины в нижние 2 элемента каждой дорожки __m256i? Если вы настраиваетесь на Ryzen, вероятно, имеет смысл снизить его до 128b. Но, возможно, vextractf128, а затем использовать перемешивание с двумя источниками (например, shufps) будет лучше на Ryzen, где перемешивание при пересечении полосы движения происходит очень медленно.   -  person Peter Cordes    schedule 24.08.2017


Ответы (1)


На Intel ваш код был бы оптимальным. Лучшее, что вы получите - это одна инструкция на 1 муп. (За исключением того, что вы можете захотеть использовать vpermps, чтобы избежать любого риска задержки обхода int / FP, если ваш вектор ввода был создан инструкцией pd, а не загрузкой или чем-то еще. Использование результата перемешивания FP в качестве ввода для целочисленных инструкций является Обычно это нормально для Intel, но я менее уверен в том, что результат инструкции FP передается целочисленному перемешиванию.)

Хотя, если вы настраиваетесь на Intel, вы можете попробовать изменить окружающий код, чтобы вы могли перетасовать нижние 64 бита каждой полосы 128b, чтобы избежать использования перемешивания с пересечением полос. (Тогда вы можете просто использовать vshufps ymm, или, если вы настраиваете KNL, vpermilps, поскольку 2 входа vshufps медленнее.)

В AVX512 есть _ 6_ (vpmovqd), который упаковывает элементы по полосам с усечением.


На Ryzen перестановка при пересечении полос идет медленно. Agner Fog не имеет номеров для vpermd, но он перечисляет vpermps (который, вероятно, использует то же оборудование для внутренних целей) при 3 мупа, задержке 5 с, по одному на пропускную способность 4 с.

vextractf128 xmm, ymm, 1 очень эффективен на Ryzen (задержка 1c, пропускная способность 0,33c), что неудивительно, поскольку он уже отслеживает регистры 256b как две половины 128b. shufps также эффективен (задержка 1 с, пропускная способность 0,5 с) и позволит вам перетасовать два регистра 128b для получения желаемого результата.

Это также экономит вам 2 регистра для 2 vpermps масок перемешивания, которые вам больше не нужны.

Поэтому я бы посоветовал:

__m256d x = /* computed here */;

// Tuned for Ryzen.  Sub-optimal on Intel
__m128 hi = _mm_castpd_ps(_mm256_extractf128_pd(x, 1));
__m128 lo = _mm_castpd_ps(_mm256_castpd256_pd128(x));
__m128 odd  = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(3,1,3,1));
__m128 even = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(2,0,2,0));

На Intel использование 3 перетасовок вместо 2 дает 2/3 оптимальной пропускной способности с дополнительной задержкой 1 с для первого результата.

person Peter Cordes    schedule 24.08.2017
comment
Я измерил, что const __m128i high32 = _mm256_castsi256_si128(_mm256_permutevar8x32_epi32(_mm256_castpd_si256(x), gHigh32Permute)); быстрее, чем const __m128i high32 = _mm_castps_si128( _mm256_castps256_ps128(_mm256_permutevar8x32_ps(_mm256_castpd_ps(x), gHigh32Permute) ));. Так, может быть, есть штраф за обход с double по float? - person Serge Rogatch; 27.08.2017
comment
@SergeRogatch: Маловероятно для перемешивания. Скорее всего, vpermd просто работает иначе, чем vpermps. (Агнер не перечислил их обоих, поэтому мне пришлось угадывать). Или то, что вы используете, результат лучше, когда он исходит от целочисленного перемешивания? Тем не менее, по словам Агнера, у AMD были различия с плавающей точкой и двойной точностью для реальных математических инструкций FP. (Конечно, почти всегда неактуально, но это подсказка о внутренней реализации, например, может быть, есть какие-то дополнительные биты тегов, хранящиеся с вектором.) - person Peter Cordes; 27.08.2017
comment
Не следует ли hi и lo поменять местами __m128 odd = _mm_shuffle_ps(hi, lo, _MM_SHUFFLE(3,1,3,1));? - person Serge Rogatch; 27.08.2017
comment
@SergeRogatch: хороший улов, да, два младших элемента результата берутся из первого операнда источника. - person Peter Cordes; 27.08.2017
comment
Подтверждено при отладке: (lo, hi, ...) - правильный порядок. - person Serge Rogatch; 27.08.2017
comment
@SergeRogatch: вы сказали что-то о запутанной документации ... См. felixcloutier.com/x86/SHUFPS.html (или оригинальный Intel vol.2 PDF, из которого он был извлечен для получения инструкций, в которых диаграммы перепутались). В разделе «Работа» есть подробный псевдокод для всего, и часто есть хорошие диаграммы и таблицы. (например, для cmpps посмотрите на cmppd, потому что он начинается в алфавитном порядке, поэтому они помещают туда полезные вещи.) Онлайн-поиск встроенных функций хорош, но иногда имеет ошибку или упускает некоторые важные детали. И диаграмм в нем никогда не бывает. - person Peter Cordes; 27.08.2017