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


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

В этом примере показано, как преобразовать простой цикл, использующий директивы parallel и for OpenMP, для использования алгоритма concurrency::parallel_for среды выполнения с параллелизмом.

Пример

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

// concrt-omp-count-primes.cpp 
// compile with: /EHsc /openmp
#include <ppl.h>
#include <random>
#include <array>
#include <iostream>

using namespace concurrency;
using namespace std;

// Determines whether the input value is prime. 
bool is_prime(int n)
{
   if (n < 2)
      return false;
   for (int i = 2; i < n; ++i)
   {
      if ((n % i) == 0)
         return false;
   }
   return true;
}

// Uses OpenMP to compute the count of prime numbers in an array. 
void omp_count_primes(int* a, size_t size)
{
   if (size == 0)
      return;

   size_t count = 0;
   #pragma omp parallel for 
      for (int i = 0; i < static_cast<int>(size); ++i)
      {
         if (is_prime(a[i])) {
            #pragma omp atomic
               ++count;
         }
      }

   wcout << L"found " << count 
         << L" prime numbers." << endl;
}

// Uses the Concurrency Runtime to compute the count of prime numbers in an array. 
void concrt_count_primes(int* a, size_t size)
{
   if (size == 0)
      return;

   combinable<size_t> counts;
   parallel_for<size_t>(0, size, [&](size_t i) 
   {
      if (is_prime(a[i])) {
         counts.local()++;
      }
   });

   wcout << L"found " << counts.combine(plus<size_t>()) 
         << L" prime numbers." << endl;
}

int wmain()
{
   // The length of the array. 
   const size_t size = 1000000;

   // Create an array and initialize it with random values. 
   int* a = new int[size];

   mt19937 gen(42);
   for (size_t i = 0; i < size; ++i) {
      a[i] = gen();
   }

   // Count prime numbers by using OpenMP and the Concurrency Runtime.

   wcout << L"Using OpenMP..." << endl;
   omp_count_primes(a, size);

   wcout << L"Using the Concurrency Runtime..." << endl;
   concrt_count_primes(a, size);

   delete[] a;
}

В результате выполнения примера получается следующий результат:

  

Алгоритм parallel_for и OpenMP 3.0 допускают, чтобы тип индекса был целочисленным типом со знаком или без знака. Алгоритм parallel_for также следит, чтобы указанный диапазон не переполнял тип со знаком. OpenMP версий 2.0 и 2.5 допускает только целочисленные типы индекса со знаком. OpenMP также не проверяет диапазон индекса.

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

Дополнительные сведения об алгоритме parallel_for и других параллельных алгоритмах см. в разделе Параллельные алгоритмы. Дополнительные сведения о классе combinable см. в разделе Параллельные контейнеры и объекты.

Этот пример отличается от предыдущего тем, что работает с объектом std::array, а не с собственным массивом. Так как OpenMP версий 2.0 и 2.5 допускает целочисленные типы индекса со знаком только в конструкции parallel for, для параллельного доступа к элементам контейнера стандартной библиотеки шаблонов (STL) нельзя использовать итераторы. Библиотека параллельных шаблонов (PPL) предоставляет алгоритм Concurrency::parallel_for_each, который параллельно выполняет задачи в итеративном контейнере, например предоставленные библиотекой STL. В нем используется такая же логика секционирования, что и в алгоритме parallel_for. Алгоритм parallel_for_each похож на алгоритм std::for_each из библиотеки STL, но алгоритм parallel_for_each выполняет задачи параллельно.

// Uses OpenMP to compute the count of prime numbers in an  
// array object. 
template<size_t Size>
void omp_count_primes(const array<int, Size>& a)
{
   if (a.size() == 0)
      return;

   size_t count = 0;
   int size = static_cast<int>(a.size());
   #pragma omp parallel for 
      for (int i = 0; i < size; ++i)
      {
         if (is_prime(a[i])) {
            #pragma omp atomic
               ++count;
         }
      }

   wcout << L"found " << count 
         << L" prime numbers." << endl;
}

// Uses the Concurrency Runtime to compute the count of prime numbers in an  
// array object. 
template<size_t Size>
void concrt_count_primes(const array<int, Size>& a)
{
   if (a.size() == 0)
      return;

   combinable<size_t> counts;
   parallel_for_each(begin(a), end(a), [&counts](int n) 
   {
      if (is_prime(n)) {
         counts.local()++;
      }
   });

   wcout << L"found " << counts.combine(plus<size_t>()) 
         << L" prime numbers." << endl;
}

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

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

cl.exe /EHsc /openmp concrt-omp-count-primes.cpp

См. также

Основные понятия

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

Параллельные алгоритмы

Параллельные контейнеры и объекты