Каждый поток будет иметь свою собственную копию 'delta' в своем кадре стека:
#pragma omp parallel shared(delta, A, B, rows, colms) private(i, j)
{
double local_delta; // one copy per thread
__omp_init_schedule(1, rows, &lb, &ub);
for (i=lb; i<=ub; i++) {
for (j=1; j<=colms; j++) {
local_delta += fabs(A[i][j]- B[i][j]);
}
}
__omp_reduce(&delta, local_delta); // accumulate thread's delta with shared var
__omp_barrier(); // do the barrier of the for construct
}
Пожалуйста, примите вышесказанное как псевдокод. Фактический шаблон кода будет зависеть от реализации, встраивания и других видов оптимизации, которые может выполнить реализация OpenMP. Если вы хотите прочитать немного о том, как все работает, посмотрите на [1] и [2].
Реализация __omp_reduce()
может быть либо чем-то основанной на дереве, либо последовательной, используя либо блокировки, либо атомарные инструкции. Реализации OpenMP обычно довольно умны и выбирают правильный алгоритм для машины и / или количества используемых потоков.
Выполнение модификации delta[numthreads]
, вероятно, приведет к снижению производительности более чем в 100 раз, поскольку это типичный пример ложного разделения, поскольку delta[0]
для потока 0 и delta[1]
для первого потока будут находиться в одной строке кэша, и это вызывает много трафика на кеш и память. Лучше было бы ввести паттинг delta[numthreads * 8]
(при условии, что delta
равен 8 байтам), чтобы каждый поток получал свою собственную строку кэша. Однако тогда вам все еще нужно выполнить окончательное агрегирование, и, вероятно, реализация OpenMP по-прежнему будет работать лучше.
[1] https://www.dontknow.de/openmp-stuff/the-thing-from-another-world-or-how-do-openmp-compilers-work-part-1/
[2] https://www.dontknow.de/openmp-stuff/thunk-you-very-much-or-how-do-openmp-compilers-work-part-2/