Существует ли инструкция simd / встроенная / встроенная для частичного смещения элементов? - PullRequest
0 голосов
/ 08 января 2020

Минимальный пример был бы более полезным:

Скажем, у меня есть отсортированные 8 целых = {10, 20, 30, 40, 50, 60, 70, 80} (Мой вариант использования для отсортированных целых чисел, но я не уверен, что эта информация полезна, учитывая векторное действие инструкции на весь набор данных)

Требуется выполнить несколько операций:

  1. Вставить и сдвинуть.

-> вставить 25 в отсортированном месте. -> становится вставкой 25 с индексом 2 и сменой покоя.

10, 20, 30, 40, 50, 60, 70, 80 становится: 10, 20, 25, 30, 40, 50, 60, 70

Снять, сдвинуть и вставить обратно.

-> удалить 20 из массива и вставить 90 сзади, если 20 найдено и удалено. 10, 20, 30, 40, 50, 60, 70, 80 становится 10, 30, 40, 50, 60, 70, 80, 90

Или набор инструкций заставит его работать?

Я пытаюсь вставить и сдвинуть часть с несколькими шагами для сортируемого массива по убыванию. https://godbolt.org/z/_WCxkW

1 Ответ

1 голос
/ 09 января 2020

Один общий подход к выполнению того, что вы хотите, (общая идея такая же для [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 , В противном случае вам нужно выполнить некоторые побитовые операции и / и / или операции.

...