Как выполнить косвенную нагрузку (собирать-разброс) в инструкциях AVX или SSE?

Я искал какое-то время, но не могу найти ничего полезного в документации или на SO. Этот вопрос действительно не помог мне, так как он делает ссылки на изменение сборки, и я пишу на C.

У меня есть код, делающий косвенный доступ, который я хочу векторизовать.

for (i = 0; i < LENGTH; ++i) {
   foo[bar[i]] *= 2;
}

Поскольку у меня есть индексы, которые я хочу удвоить внутри bar, мне было интересно, есть ли способ загрузить эти индексы foo в векторный регистр, а затем я мог бы применить свою математику и сохранить ее обратно к тем же индексам.

Что-то вроде следующего. Инструкции по load и store я только что составил, потому что я не мог найти ничего подобного в документации AVX или SSE. Я думаю, что я где-то читал, что AVX2 имеет схожие функции, но процессор, с которым я работаю, не поддерживает AVX2.

for (i = 0; i < LENGTH; i += 8) {
   // For simplicity, I'm leaving out any pointer type casting
   __m256 ymm0 = _mm256_load_indirect(bar+i);
   __m256 ymm1 = _mm256_set1_epi32(2); // Set up vector of just 2's
   __m256 ymm2 = _mm256_mul_ps(ymm0, ymm1);
   _mm256_store_indirect(ymm2, bar+i);
}

Существуют ли какие-либо инструкции в AVX или SSE, которые позволят мне загрузить векторный регистр с массивом индексов из другого массива? Или любые "хакерские" способы обойти его, если нет явной функции?

Ответ 1

(Я пишу ответ на этот старый вопрос, так как думаю, что он может помочь другим.)

Короткий ответ

Нет. В наборах команд SSE и AVX нет инструкций разброса/сбора.

Более длинный ответ

Команды разброса/сбора данных являются дорогостоящими для реализации (с точки зрения сложности и степени использования кремния), потому что механизм разброса/сбора должен быть тесно переплетен с контроллером кэш-памяти. Я считаю, что это причина того, что эта функциональность отсутствовала в SSE/AVX.

Для более новых наборов инструкций ситуация иная. В AVX2 у вас есть

  • VGATHERDPD, VGATHERDPS, VGATHERQPD, VGATHERQPS для сбора с плавающей запятой (здесьвстроено)
  • VPGATHERDD, VPGATHERQD, VPGATHERDQ, VPGATHERQQ для целочисленной сборки (здесьвстроено)

В AVX-512 мы получили

  • VSCATTERDPD, VSCATTERDPS, VSCATTERQPD, VSCATTERQPS для рассеяния с плавающей запятой (встроенные здесь)
  • VPSCATTERDD, VPSCATTERQD, VPSCATTERDQ, VPSCATTERQQ для целочисленного рассеяния (здесьвстроено)

Тем не менее, все еще остается вопрос, будет ли использование scatter/collect для такой простой операции окупиться.