Да, как вы объясните, как «Христо Ильев» хотел, чтобы он работал. К сожалению, у него было несколько проблем с задачами:
Его так называемый «барьер» с использованием taskwait
работает только для потока, создавшего задачи. Все остальные потоки проходят через него. Поэтому его полное сокращение в конце бесполезно, потому что большинство потоков выполняют его до, они фактически выполняют любую работу. «Taskwait» был разработан, чтобы помочь учитывать зависимости данных (очень полезно in recursive functions!), Поэтому он работает так, как он делает.
Единственная причина, по которой программа работает иногда даже без функционального сокращения в конце, заключается в том, что его первая ошибка частично отменена второй ошибкой: переменная t_worst_q
в задаче ссылается на одну из потока, который сгенерирован задача и, следовательно, действует как глобальная переменная. Но тогда, конечно, у нас есть условие гонки в std::min
, что по-прежнему позволяет сбой кода иногда.
Таким образом, в силу его код эквивалентен следующему:
#pragma omp parallel
{
// Create tasks
#pragma omp single nowait
{
for(std::set<size_t>::const_iterator it=mesh->NEList[vid].begin();
it!=mesh->NEList[vid].end(); ++it) {
size_t elem = *it;
#pragma omp task shared(worst_q)
worst_q = std::min(worst_q, mesh->element_quality(elem));
}
}
}
Это, как было сказано выше, имеет проблему наличия условия гонки в доступе к worst_q
при выполнении std::min
. Если функция mesh->element_quality
является вычислительно дорогой по сравнению с функцией std::min
, то простым решением является критический участок вокруг части std::min
(после выполнения mesh->element_quality
вне критической секции). Если, с другой стороны, mesh->element_quality
не дороже, чем std::min
, то критическая секция убьет параллельность. Но в этом случае функция mesh->element_quality
настолько дешева, что накладные расходы на управление задачами все равно съедят все возможные ускорения.
Этот ответ был ужасно неправильным. Я только что исправил это. –