Один общий подход к выполнению того, что вы хотите, (общая идея такая же для [u]int_{8,16,32,64}
или даже float
/ double
):
Вставьте x
в input
:
// Shift your input array (e.g. "abcefghi") to the right:
out = ShiftRight(input); // out = 0abcefgh
// broadcast the to-be-inserted element (e.g., 'd')
insert = broadcast(x); // insert = dddddddd
// compute
out = min(max(out,insert),input)
// == min(max(0abcefgh,dddddddd),abcefghi)
// == min(ddddefgh,abcefghi) == abcdefgh
Удалить первый элемент, не меньший x
из input
:
// shift input (e.g., "abcdefgh") to the left (insert something at the end)
out = ShiftLeft(input); // out = bcdefghX
// determine elements smaller than `x` (e.g., "f") by broadcast and compare
mask = broadcast(x) < input; // mask = 11111000
// take masked elements from `input` and other values from `out` (using a blend instruction)
out = blend(mask, input, out); // == abcdeghX
Если количество удаляемых элементов не гарантируется равным 1 (т. Е. Оно может не существует или существует несколько раз), это более сложно, поскольку каждое выходное значение потенциально зависит от каждого входного значения. Одной из идей может быть сравнение на равенство и подсчет количества элементов (используя maskmove
и popcount
).
Для сдвига вы можете использовать
- SSE2 и только один 128-битный регистр:
pslldq
, psrldq
- SSSE3 и последовательность 128-битных регистров:
palignr
- AVX2 и один 256-битный регистр:
vpermd
с предварительно определенным индексным вектором (нет AVX2-эквивалента предыдущих инструкций, который работает для всего 256-битного регистра) - Если ваш ввод хранится в памяти, загрузите его снова с одним смещением элемента (для этого требуется «безопасный» элемент за каждым концом массива - и это может привести к значительной задержке записи-чтения, если вы выполняете эти операции несколько раз)
Для вещания я предлагаю просто использовать _mm[256]_set1_epi32
intrinsi c и позволить компилятору выяснить, что наиболее эффективно (без AVX2 это, вероятно, потребует случайного воспроизведения)
Операторы Min / Max существуют для различных Размеры / типы (в зависимости от версии SSE / AVX) - просто ищите инструкции, начинающиеся с pmin
/ pmax
.
Насколько я знаю, до AVX512 нет сравнений без знака, но Конечно, вы можете использовать сравнение со знаком, если никакие значения не превышают наибольшее значение со знаком. Или вы можете обойти это, щелкнув верхний бит перед сравнением (я полагаю, что есть связанный с стеком поток вопрос).
Наконец, смешивание выполняется с помощью pblendvb
, если у вас SSE4.1 , В противном случае вам нужно выполнить некоторые побитовые операции и / и / или операции.