建議使用 Visual Studio 2017

如何:轉換使用取消的 OpenMP 迴圈來使用並行執行階段

 

如需 Visual Studio 2017 的最新文件請參閱 Visual Studio 2017 文件

某些平行迴圈不需要所有反覆項目都執行。 例如,搜尋值的演算法可以在找到值後終止。 OpenMP 未提供中斷平行迴圈的機制。 不過,您可以使用布林值 (或旗標),讓迴圈反覆項目表示已找到解決方案。 並行執行階段提供可讓某個工作取消其他尚未啟動之工作的功能。

這個範例示範如何將不需要所有反覆項目執行的 OpenMP parallel for 迴圈,轉換為使用並行執行階段取消機制。

這個範例使用 OpenMP 和並行執行階段,實作 std::any_of 演算法的平行版本。 此範例的 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-parallel-any-of.cpp 的檔案中,然後在 Visual Studio 的 [命令提示字元] 視窗中執行下列命令。

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

從 OpenMP 移轉至並行執行階段
取消
平行演算法

顯示: