Я ищу несколько советов о том, как сделать параллельную префиксную сумму с SSE. Я заинтересован в том, чтобы делать это в массиве int, float или doubleles.
Я придумал два решения. Частный случай и общий случай. В обоих случаях решение проходит через массив через два прохода параллельно с OpenMP. Для специального случая я использую SSE на обоих проходах. В общем случае я использую его только на втором проходе.
Мой главный вопрос: как я могу использовать SSE на первом проходе в общем случае? Следующая ссылка simd-prefix-sum-on-intel-cpu показывает улучшение для байтов, но не для 32-битных типов данных.
Причина, по которой особый случай называется special, заключается в том, что он требует, чтобы массив находился в специальном формате. Например, допустим, что было только 16 элементов массива a
для float. Затем, если массив был перестроен следующим образом (массив структур для структуры массивов):
a[0] a[1] ...a[15] -> a[0] a[4] a[8] a[12] a[1] a[5] a[9] a[13]...a[3] a[7] a[11] a[15]
На обоих проходах можно использовать вертикальные суммы SSE. Однако это было бы эффективно только в том случае, если массивы уже были в специальном формате, и выход можно было использовать в специальном формате. В противном случае дорогостоящий перерасчет должен был бы выполняться как на входе, так и на выходе, что сделало бы его намного медленнее, чем в общем случае.
Возможно, мне стоит рассмотреть другой алгоритм для суммы префикса (например, двоичное дерево)?
Код для общего случая:
void prefix_sum_omp_sse(double a[], double s[], int n) {
double *suma;
#pragma omp parallel
{
const int ithread = omp_get_thread_num();
const int nthreads = omp_get_num_threads();
#pragma omp single
{
suma = new double[nthreads + 1];
suma[0] = 0;
}
double sum = 0;
#pragma omp for schedule(static) nowait //first parallel pass
for (int i = 0; i<n; i++) {
sum += a[i];
s[i] = sum;
}
suma[ithread + 1] = sum;
#pragma omp barrier
#pragma omp single
{
double tmp = 0;
for (int i = 0; i<(nthreads + 1); i++) {
tmp += suma[i];
suma[i] = tmp;
}
}
__m128d offset = _mm_set1_pd(suma[ithread]);
#pragma omp for schedule(static) //second parallel pass with SSE as well
for (int i = 0; i<n/4; i++) {
__m128d tmp1 = _mm_load_pd(&s[4*i]);
tmp1 = _mm_add_pd(tmp1, offset);
__m128d tmp2 = _mm_load_pd(&s[4*i+2]);
tmp2 = _mm_add_pd(tmp2, offset);
_mm_store_pd(&s[4*i], tmp1);
_mm_store_pd(&s[4*i+2], tmp2);
}
}
delete[] suma;
}