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
.