Поделиться через


Практическое руководство. Преобразование цикла OpenMP, использующего отмену для использования среды выполнения с параллелизмом

Некоторые параллельные циклы не требуют выполнения всех итераций. Например, алгоритм, который ищет значение, может завершиться после обнаружения значения. OpenMP не предоставляет механизм для прерывания параллельного цикла. Однако можно использовать логическое значение или флаг, чтобы включить итерацию цикла, чтобы указать, что решение найдено. Среда выполнения параллелизма предоставляет функциональные возможности, позволяющие одной задаче отменить другие задачи, которые еще не запущены.

В этом примере показано, как преобразовать параллельOpenMP для цикла, который не требует выполнения всех итераций для использования механизма отмены среды выполнения параллелизма.

Пример

В этом примере используется openMP и среда выполнения параллелизма для реализации параллельной версии алгоритма std::any_of . Версия OpenMP этого примера использует флаг для координации всех параллельных итераций цикла, которые выполнены условием. Версия, использующая среду выполнения параллелизма, использует метод параллелизма::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, все итерации цикла выполняются, даже если флаг установлен. Кроме того, если задача должна была иметь какие-либо дочерние задачи, флаг также должен быть доступен для этих дочерних задач для обмена данными об отмене. В среде выполнения параллелизма при отмене группы задач среда выполнения отменяет все дерево работы, включая дочерние задачи. Алгоритм параллелизма::p arallel_for_each использует задачи для выполнения работы. Поэтому, когда одна итерация цикла отменяет корневую задачу, все дерево вычислений также отменяется. При отмене дерева работы среда выполнения не запускает новые задачи. Однако среда выполнения позволяет выполнять задачи, которые уже начали выполняться. Таким образом, в случае алгоритма parallel_for_each активные итерации цикла могут очистить свои ресурсы.

В обоих версиях этого примера, если массив содержит несколько копий значения для поиска, несколько итераций цикла могут одновременно задать результат и отменить общую операцию. Вы можете использовать примитив синхронизации, например критически важный раздел, если ваша проблема требует выполнения только одной задачи при выполнении условия.

Вы также можете использовать обработку исключений для отмены задач, использующих PPL. Дополнительные сведения об отмене см. в разделе "Отмена" в PPL.

Дополнительные сведения о parallel_for_each и других параллельных алгоритмах см. в разделе "Параллельные алгоритмы".

Компиляция кода

Скопируйте пример кода и вставьте его в проект Visual Studio или вставьте его в файл с именем concrt-omp-parallel-any-of.cpp , а затем выполните следующую команду в окне командной строки Visual Studio.

cl.exe /EHsc /openmp concrt-omp-parallel-any-of.cpp

См. также

Переход от OpenMP к среде выполнения с параллелизмом
Отмена в библиотеке параллельных шаблонов
Параллельные алгоритмы