Тасование по маске с Intel AVX

Я новичок в программировании AVX. У меня есть регистр, который необходимо перетасовать, точнее, я хочу переместить несколько байтов из 256-битного регистра R1 в пустой регистр R2. Я хочу определить маску, которая сообщает операции тасования, какой байт из старого регистра R1 должен быть скопирован в какое место в новом регистре.

Маска должна выглядеть следующим образом (Src:Byte Pos в R1, Target:Byte Pos в R2):

{(0,0),(1,1),(1,4),(2,5),...}

Это означает, что несколько байтов копируются дважды.

Я не уверен на 100%, какую функцию мне следует использовать для этого. Я попробовал немного с этими двумя функциями AVX, вторая просто использует 2 полосы.

__m256 _mm256_permute_ps (__m256 a, int imm8)
__m256 _mm256_shuffle_ps (__m256 a, __m256 b, const int imm8)

Я полностью запутался в маске Shuffle в imm8 и в том, как создать ее так, чтобы она работала, как описано выше.

Я посмотрел на этих слайдах(стр. 26), где описано _MM_SHUFFLE, но я не нашел решения с этим для моей проблемы.

Есть ли уроки, как создать такую ​​маску? Или такие примеры функций для двух методов, чтобы глубже понять их?

Заранее спасибо за подсказки

1 ответ

Решение

TL:DR: вам, вероятно, понадобится несколько тасов для обработки пересечения линий, или если ваш паттерн продолжается точно так же, как вы можете использовать _mm256_cvtepu16_epi32 (vpmovzxwd) а потом _mm256_blend_epi16,


Для тасов x86 (как я думаю, как и большинства наборов инструкций SIMD) позиция назначения неявна. Константа случайного управления просто имеет исходные индексы в порядке назначения, будь то imm8 который компилируется + собирается прямо в инструкции asm или это вектор с индексом в каждом элементе.

Каждая позиция назначения читает ровно одну исходную позицию, но одну и ту же исходную позицию можно прочитать более одного раза. Каждый целевой элемент получает значение из источника случайного выбора.

См. Преобразование _mm_shuffle_epi32 в выражение C для перестановки? для простой версии C dst = _mm_shuffle_epi32(src, _MM_SHUFFLE(d,c,b,a)), показывающий, как используется управляющий байт.

(За pshufb / _mm_shuffle_epi8 элемент с установленным старшим битом обнуляет эту позицию назначения вместо чтения какого-либо исходного элемента, но другие тасовки x86 игнорируют все старшие биты в векторах управления тасованием.)

Без маскирования слиянием AVX512 не может быть тасовок, которые бы смешивались с пунктом назначения. Есть некоторые тасовки из двух источников, такие как _mm256_shuffle_ps (vshufps), который может перемешивать элементы из двух источников для получения одного вектора результатов. Если вы хотите оставить некоторые элементы назначения неписанными, вам, вероятно, придется перемешать, а затем смешать, например, с _mm256_blendv_epi8 или если вы можете использовать смесь с 16-битной гранулярностью, вы можете использовать более эффективную немедленную смесь _mm256_blend_epi16 или даже лучше _mm256_blend_epi32 (AVX2 vpblendd так же дешево, как _mm256_and_si256 на процессорах Intel, и это лучший выбор, если вам вообще нужно смешивать, если он может выполнить свою работу; см. http://agner.org/optimize/)


Для вашей проблемы (без AVX512VBMI vpermb в Cannonlake), вы не можете перетасовать отдельные байты из 16-ти младших "дорожек" в верхние 16 "дорожек" __m256i вектор с одной операцией.

Перестановки AVX не похожи на полную 256-битную SIMD, они больше похожи на две 128-битные операции параллельно. Единственное исключение - некоторые перестановки пересекающих полосы AVX2 с 32-битной гранулярностью или больше, например vpermd (_mm256_permutevar8x32_epi32). А также версии AVX2 pmovzx / pmovsx например, pmovzxbq расширяет ли ноль младшие 4 байта регистра XMM на 4 qwords регистра YMM, а не младшие 2 байта каждой половины регистра YMM. Это делает его намного более полезным с операндом источника памяти.

Но в любом случае, версия AVX2 pshufb (_mm256_shuffle_epi8) выполняет две отдельные перестановки 16x16 байтов в двух дорожках 256-битного вектора.


Вы, вероятно, захотите что-то вроде этого:

// Intrinsics have different types for integer, float and double vectors
// the asm uses the same registers either way
__m256i  shuffle_and_blend(__m256i dst, __m256i src)
{
    // setr takes element in low to high order, like a C array init
    // unlike the standard Intel notation where high element is first
    const __m256i  shuffle_control = _mm256_setr_epi8(
          0,      1,  -1, -1,   1,      2, ...);
    // {(0,0),  (1,1), (zero)  (1,4), (2,5),...}  in your src,dst notation
    // Use -1 or 0x80 or anything with the high bit set
    //  for positions you want to leave unmodified in dst
   // blendv uses the high bit as a blend control, so the same vector can do double duty

    // maybe need some lane-crossing stuff depending on the pattern of your shuffle.
    __m256i  shuffled = _mm256_shuffle_epi8(src, shuffle_control);

    // or if the pattern continues, and you're just leaving 2 bytes between every 2-byte group:
    shuffled = _mm256_cvtepu16_epi32(src);  // if src is a __m128i

    __m256i  blended = _mm256_blendv_epi8(shuffled, dst, shuffle_control);
    // blend dst elements we want to keep into the shuffled src result.
    return blended;
}    

Обратите внимание, что pshufb нумерация начинается с 0 для 2-х 16-ти байтов. Две половинки __m256i могут отличаться, но они не могут читать элементы из другой половины. Если вам нужны позиции в верхнем ряду, чтобы получить байты из нижнего ряда, вам потребуется больше перемешивания + смешивания (например, включая vinserti128 или же vperm2i128 или, может быть, vpermd пересекая полосу (dword shuffle), чтобы собрать все необходимые байты в одну 16-байтовую группу в некотором порядке.

(На самом деле _mm256_shuffle_epi8 (PSHUFB) игнорирует биты 4..6 в индексе тасования, поэтому запись 17 такой же как 1, но очень вводит в заблуждение. Это эффективно делает %16 До тех пор, пока старший бит не установлен. Если старший бит установлен в векторе случайного управления, он обнуляет этот элемент. Нам не нужна эта функциональность здесь; _mm256_blendv_epi8 не заботится о старом значении заменяемого элемента)

В любом случае, этот простой пример с 2 инструкциями работает только в том случае, если шаблон не продолжается. Если вам нужна помощь в разработке ваших настоящих перемешиваний, вам придется задать более конкретный вопрос.


И кстати, я заметил, что ваш шаблон смешивания использовал 2 новых байта, а затем 2 пропущенных 2. Если это продолжается, вы можете использовать vpblendw_mm256_blend_epi16 вместо blendv, потому что эта инструкция выполняется только 1 моп вместо 2 на процессорах Intel. Это также позволит вам использовать AVX512BW vpermw 16-разрядный тасовщик, доступный в современных процессорах Skylake-AVX512, вместо, возможно, даже более медленного AVX512VBMI vpermb,

Или на самом деле, возможно, это позволит вам использовать vpmovzxwd (_mm256_cvtepu16_epi32) для расширения нуля 16-битных элементов до 32-битных, в качестве перестановки пересекающих полосы. Затем смешайте с dst,

Другие вопросы по тегам