Практическое руководство. Преобразование цикла OpenMP, использующего отмену для использования среды выполнения с параллелизмом
В некоторых параллельных циклах не обязательно выполнять все итерации.Например, алгоритм, ищущий значение, может завершить работу, когда значение найдено.В OpenMP нет механизма прекращения параллельного цикла.Тем не менее, можно использовать логическое значение, флаг или позволить итерации цикла указывать, что решение найдено.Среда выполнения с параллелизмом содержит функцию, позволяющую одной задаче отменять другие, еще не начатые, задачи.
В этом примере показано, как преобразовать цикл OpenMP parallelfor, для которого не требуется выполнение всех итераций, для выполнения с использованием механизма отмены среды выполнения с параллелизмом.
Пример
В этом примере для реализации параллельной версии алгоритма std::any_of используется как OpenMP, так и среда выполнения с параллелизмом.В версии этого примера, использующей OpenMP, для сообщения всем параллельным итерациям цикла о том, что выполнено некое условие, применяется флаг.Использует версию, которая используется во время выполнения параллельной обработки concurrency::structured_task_group::cancel метод для остановки операции при выполнении условия.
// concrt-omp-parallel-any-of.cpp
// compile with: /EHsc /openmp
#include <ppl.h>
#include <array>
#include <random>
#include <iostream>
using namespace concurrency;
using namespace std;
// Uses OpenMP to determine whether a condition exists in
// the specified range of elements.
template <class InIt, class Predicate>
bool omp_parallel_any_of(InIt first, InIt last, const Predicate& pr)
{
typedef typename std::iterator_traits<InIt>::value_type item_type;
// A flag that indicates that the condition exists.
bool found = false;
#pragma omp parallel for
for (int i = 0; i < static_cast<int>(last-first); ++i)
{
if (!found)
{
item_type& cur = *(first + i);
// If the element satisfies the condition, set the flag to
// cancel the operation.
if (pr(cur)) {
found = true;
}
}
}
return found;
}
// Uses the Concurrency Runtime to determine whether a condition exists in
// the specified range of elements.
template <class InIt, class Predicate>
bool concrt_parallel_any_of(InIt first, InIt last, const Predicate& pr)
{
typedef typename std::iterator_traits<InIt>::value_type item_type;
structured_task_group tasks;
// Create a predicate function that cancels the task group when
// an element satisfies the condition.
auto for_each_predicate = [&pr, &tasks](const item_type& cur) {
if (pr(cur)) {
tasks.cancel();
}
};
// Create a task that calls the predicate function in parallel on each
// element in the range.
auto task = make_task([&]() {
parallel_for_each(first, last, for_each_predicate);
});
// The condition is satisfied if the task group is in the cancelled state.
return tasks.run_and_wait(task) == canceled;
}
int wmain()
{
// The length of the array.
const size_t size = 100000;
// Create an array and initialize it with random values.
array<int, size> a;
generate(begin(a), end(a), mt19937(42));
// Search for a value in the array by using OpenMP and the Concurrency Runtime.
const int what = 9114046;
auto predicate = [what](int n) -> bool {
return (n == what);
};
wcout << L"Using OpenMP..." << endl;
if (omp_parallel_any_of(begin(a), end(a), predicate))
{
wcout << what << L" is in the array." << endl;
}
else
{
wcout << what << L" is not in the array." << endl;
}
wcout << L"Using the Concurrency Runtime..." << endl;
if (concrt_parallel_any_of(begin(a), end(a), predicate))
{
wcout << what << L" is in the array." << endl;
}
else
{
wcout << what << L" is not in the array." << endl;
}
}
В результате выполнения примера получается следующий результат:
Using OpenMP...
9114046 is in the array.
Using the Concurrency Runtime...
9114046 is in the array.
В версии, использующей OpenMP, все итерации цикла выполняются, даже когда флаг установлен.Поэтому если у задачи есть дочерние задачи, флаг будет доступен для этих дочерних задач, чтобы они могли узнать об отмене.В среде выполнения с параллелизмом при отмене задачи среда отменяет все дерево работы, включая дочерние задачи.Concurrency::parallel_for_each алгоритм использует задачи для выполнения работы.Поэтому если одна итерация цикла отменяет корневую задачу, отменяется все дерево вычислений.При отмене дерева работы среда выполнения не запускает новые задачи.При этом среда выполнения позволяет завершиться всем начатым задачам.Таким образом, в случае алгоритма parallel_for_each активные итерации цикла могут очистить свои ресурсы.
В обоих версиях примера несколько итераций цикла могут одновременно задавать результат и отменять общую операцию, если массив содержит более одной копии искомого значения.Можно использовать примитив синхронизации, например критичный участок, если для решения проблемы нужно, чтобы при выполнении условия только одна задача выполняла работу.
Для отмены задач, использующих PPL, также можно использовать обработку исключений.Дополнительные сведения об отмене см. в разделе Отмена в библиотеке параллельных шаблонов.
Дополнительные сведения об алгоритме parallel_for_each и других параллельных алгоритмах см. в разделе Параллельные алгоритмы.
Компиляция кода
Скопируйте код и вставьте его в проект Visual Studio или вставьте его в файл с именем concrt-omp параллельный любой of.cpp и запустите следующую команду в окне командной строки Visual Studio.
cl.exe /EHsc /openmp concrt-omp-parallel-any-of.cpp
См. также
Основные понятия
Переход от OpenMP к среде выполнения с параллелизмом