———————РЕДАКТИРОВАТЬ————————-
Я отредактировал код следующим образом:
#pragma omp parallel for private(i, piold, err) shared(threshold_err) reduction(+:pi) schedule (static)
{
for (i = 0; i < 10000000000; i++){ //1000000000//705035067
piold = pi;
pi += (((i&1) == false) ? 1.0 : -1.0)/(2*i+1);
err = fabs(pi-piold);
if ( err < threshold_err){
#pragma omp cancel for
}
}
}
pi = 4*pi;
Я компилирую это с LLVM3.9 / Clang4.0. Когда я запускаю его с одним потоком, я получаю ожидаемые результаты с действием отмены прагмы (проверено на версии без отмены прагмы, что приводит к более быстрому запуску).
Но когда я запускаю его с потоками> = 2, программа зацикливается. Я запускаю код на машинах NUMA. Что происходит? Возможно, условие отмены не выполняется! Но тогда код занимает больше времени, чем однопотоковая версия без прагмы !! К вашему сведению, он запускает файл, когда OMP_CANCELLATION = false.
У меня есть следующий код OpenMP. Я использую LLVM-3.9 / Clang-4.0 для компиляции этого кода.
#pragma omp parallel private(i, piold, err) shared(pi, threshold_err)
{
#pragma omp for reduction(+:pi) schedule (static)
for (i = 0; i < 10000000 ; i++){
piold = pi;
pi += (((i&1) == false) ? 1.0 : -1.0)/(2*i+1);
#pragma omp critical
{
err = fabs(pi-piold);// printf("Err: %0.11f\n", err);
}
if ( err < threshold_err){
printf("Cancelling!\n");
#pragma omp cancel for
}
}
}
К сожалению, я не думаю, что #pragma omp cancel for
завершает весь for
петля. Я распечатываю err
значение в конце, но опять же с параллелизмом это сбивает с толку, какое значение печатается. Окончательное значение err
меньше чем threshold_err
, Отмена печати — это печать, но в самом начале программы, что удивительно. Программа продолжает работать после этого!
Как убедиться, что это правильная реализация? Кстати, для OMP_CANCELLATION установлено значение true, и небольшая тестовая программа возвращает «1» для соответствующей функции omp_get_cancellation ().
Я понимаю, что отмена omp — это просто сигнал прерывания, он уведомляет, чтобы потом не создавался поток. Потоки, которые все еще работают, будут продолжаться до конца. Увидеть http://bisqwit.iki.fi/story/howto/openmp/ а также http://jakascorner.com/blog/2016/08/omp-cancel.html
На самом деле, на мой взгляд, я вижу в вашем программном продукте приемлемое приближение. Тем не менее, некоторые переменные можно хранить в меньшем объеме. Это мое предложение
#include <iostream>
#include <cmath>
#include <iomanip>
int main() {
long double pi = 0.0;
long double threshold_err = 1e-7;
int cancelFre = 0;
#pragma omp parallel shared(pi, threshold_err, cancelFre)
{
#pragma omp for reduction(+:pi) schedule (static)
for (int i = 0; i < 100000000; i++){
long double piold = pi;
pi += (((i&1) == false) ? 1.0 : -1.0)/(2*i+1);
long double err = std::fabs(pi-piold);
if ( err < threshold_err){
#pragma omp cancel for
cancelFre++;
}
}
}
std::cout << std::setprecision(10) << pi * 4 << " " << cancelFre;
return 0;
}
Хорошо, так что я решил это. В моем коде выше проблема была здесь:
err = fabs(pi-piold);
В приведенной выше строке pi
изменяется до следующего, если условие изменяется. Также несколько потоков делают то же самое. Как я понимаю, это заставляет программу идти в тупик.
Я решил это, заставив только один поток, мастер, сделать эту проверку:
if(omp_get_thread_num()==0){
err = fabs(pi-piold);
if ( err < threshold_err){
#pragma omp cancel for
}
}
Я мог бы использовать #pragma omp single
но это дало ошибку о вложенных прагмах.
Здесь производительность страдает при небольшом количестве потоков (1-4 хуже, чем обычный последовательный код). После этого производительность улучшается. Это не лучшее решение, и кто-то может улучшить это.