如何:轉換使用取消的 OpenMP 迴圈來使用並行執行階段
有些平行迴圈不需要執行所有反復專案。 例如,搜尋值的演算法可以在找到值之後終止。 OpenMP 不提供中斷平行迴圈的機制。 不過,您可以使用布林值或旗標來啟用迴圈反復專案,以指出找到解決方案。 並行執行時間提供的功能可讓一項工作取消尚未啟動的其他工作。
此範例示範如何轉換不需要執行所有反復專案的 OpenMP 平行 for 迴圈,以使用並行執行時間取消機制。
範例
此範例同時使用 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 的 版本中,即使已設定旗標,迴圈的所有反復專案也會執行。 此外,如果工作要有任何子工作,旗標也必須可供這些子工作用來傳達取消。 在並行執行時間中,當工作組取消時,執行時間會取消整個工作樹狀結構,包括子工作。 concurrency::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