Я пытаюсь оптимизировать следующий цикл с помощью OpenMP:
#pragma omp parallel for private(diff)
for (int j = 0; j < x.d; ++j) {
diff = x(example,j) - x(chosen_pts[ndx - 1],j);
#pragma omp atomic
d2 += diff * diff;
}
Но он работает на 4 раза медленнее, чем без #pragma
.
РЕДАКТИРОВАТЬ
Как отметил Петр С., совпадение и эрнен, в моем случае xd
настолько мала, что параллелизм заставляет мой код работать медленнее. Я также отправляю внешний цикл, возможно, есть много возможностей для многопоточности: (xn превышает 100 миллионов)
float sum_distribution = 0.0;
// look for the point that is furthest from any center
float max_dist = 0.0;
for (int i = 0; i < x.n; ++i) {
int example = dist2[i].second;
float d2 = 0.0, diff;
//#pragma omp parallel for private(diff) reduction(+:d2)
for (int j = 0; j < x.d; ++j) {
diff = x(example,j) - x(chosen_pts[ndx - 1],j);
d2 += diff * diff;
}
if (d2 < dist2[i].first) {
dist2[i].first = d2;
}
if (dist2[i].first > max_dist) {
max_dist = dist2[i].first;
}
sum_distribution += dist2[i].first;
}
Если кому-то интересно, вот целая функция: https://github.com/ghamerly/baylorml/blob/master/fast_kmeans/general_functions.cpp#L169, но по мере того, как я измерял, 85% прошедшего времени поступает из этого цикла.
Да, внешний цикл, как указано, может быть распараллелен с помощью OpenMP. Все переменные, измененные в цикле, являются либо локальными, либо итерационными или используются для агрегирования по циклу. И я полагаю, что вызовы x()
при вычислении diff
имеют побочных эффектов.
Для правильной и эффективной сборки агрегации вам необходимо использовать цикл OpenMP с предложением о reduction
. Для sum_distribution
операция сокращения равна +
, а для max_dist
max
. Таким образом, добавление следующей прагмы перед внешним циклом должно выполнять задание:
#pragma omp parallel for reduction(+:sum_distribution) reduction(max:max_dist)
Обратите внимание, что max
как операция сокращения может использоваться только с OpenMP 3.1. Это не так уж и ново, поэтому большинство компиляторов с поддержкой OpenMP уже поддерживают его, но не все; или вы можете использовать более старую версию. Поэтому имеет смысл проконсультироваться с документацией для вашего компилятора.
num_threads(1)
к прагме; удалить reduction(max:max_dist) and instead add #pragma omp critical
перед вторым оператором if
.
d2
стал узким местом: все потоки должны иметь доступ к одной и той же памяти. Быстрее было бы позволить каждому потоку отслеживать свою собственную сумму, добавляя эти специфичные для потока суммы вместе, когда цикл завершен. Я думаю, что вы можете сделать это, добавивreduction(+,d2)
к первой прагме