Я пытаюсь написать сжатие потока (взять массив и избавиться от пустых элементов) с помощью встроенных SIMD-операций. Каждая итерация цикла обрабатывает 8 элементов за раз (ширина SIMD).
С встроенными функциями SSE я могу сделать это довольно эффективно с помощью _mm_shuffle_epi8(), который выполняет поиск в 16 табличных таблицах (собирается в параллельной вычислительной терминологии). Индексы перемешивания предварительно вычисляются и просматриваются с помощью битовой маски.
for (i = 0; i < n; i += 8)
{
v8n_Data = _mm_load_si128(&data[i]);
mask = _mm_movemask_epi8(&is_valid[i]) & 0xff; // is_valid is byte array
v8n_Compacted = _mm_shuffle_epi8(v16n_ShuffleIndices[mask]);
_mm_storeu_si128(&compacted[count], v8n_Compacted);
count += bitCount[mask];
}
Моя проблема теперь я хотел бы реализовать это для Altivec SIMD тоже (не спрашивайте, почему - ошибочное бизнес-решение). Altivec не имеет эквивалента для _mm_movemask_epi8(), важного ингредиента. Итак, мне нужно будет найти способ либо
-
emulate _mm_movemask_epi8() - кажется дорогим, несколько смен и ORs
-
эффективно генерируют индексы тасования -
а именно, индекс я будет индексом i-го действительного элемента в некомпактных данных
element_valid: 0 0 1 0 1 0 0 1 0
gather_indices: x x x x x x 6 4 1
scatter_indices: 3 3 2 2 1 1 1 0 0
Просто сделать это серийно, но мне нужно, чтобы он был параллельным (SIMD). Кажется, легко генерировать индексы разброса с префиксом sum, но так как ни AltiVec, ни SSE не имеют команды рассеяния, мне нужно собирать индексы вместо этого. Собирать индексы - это обратная функция индексов рассеяния, но как это можно получить параллельно? Я знаю, что в новаторские дни программирования GPU конвертирование рассеивателей в сборники было распространенным методом, но ни один из этих двух описанных методов не кажется практичным.
Может быть, если не настаивать на том, что уплотнение сохраняет порядок элементов, это обеспечит более эффективную реализацию? Я могу это сделать.