Тасование по маске с Intel AVX - PullRequest
0 голосов
/ 30 апреля 2018

Я новичок в программировании 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 Ответ

0 голосов
/ 30 апреля 2018

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


Для x86-тасовок (как мне кажется, большинства SIMD-наборов команд), * ​​1009 * позиция назначения неявная. Константа случайного управления просто имеет исходные индексы в порядке назначения, , будь то 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 shuffle - это не полная 256-битная SIMD, а две параллельные 128-битные операции. Единственными исключениями являются некоторые тасовки с пересечением полосы AVX2 с гранулярностью 32 бита или более, например vpermd (_mm256_permutevar8x32_epi32). А также версии AVX2 pmovzx / pmovsx, например, pmovzxbq расширяет ноль 4 младших байта регистра XMM на 4 qwords регистра YMM, а не младшие 2 байта каждой половины регистра YMM. Это делает его более полезным с операндом источника памяти.

Но, в любом случае, версия pshufb (_mm256_shuffle_epi8) AVX2 выполняет два отдельных перестановок 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 для вторых 16 байтов. Две половины __m256i могут быть разными, но они не могут читать элементы из другой половины. Если вам нужны позиции в верхнем ряду, чтобы получить байты из нижнего ряда, вам понадобится больше тасования + смешивания (например, включая vinserti128 или vperm2i128, или, может быть, vpermd перестановка мечей, пересекающих полосы), чтобы получить все нужные вам байты в одну 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.

...