Como converter um loop OpenMP que usa cancelamento para usar o tempo de execução de simultaneidade
Alguns loop paralelos não requerem que todas as iterações serão executadas. Por exemplo, um algoritmo que pesquise por um valor pode terminar depois do valor for encontrado. OpenMP não fornece um mecanismo para estoirar de um loop paralelo. Entretanto, você pode usar um valor booliano, ou o sinalizador, para habilitar a iteração do loop para indicar que a solução esteve localizada. O tempo de execução de simultaneidade fornece a funcionalidade que permite uma tarefa cancelar outras tarefas que ainda não foram iniciados.
Este exemplo demonstra como converter um loop OpenMP paralelapara que não solicite que todas as iterações executa para usar o cancelamento do mecanismo de tempo de execução de simultaneidade.
Exemplo
Este exemplo usa OpenMP e o tempo de execução de simultaneidade para implementar uma versão paralela do algoritmo de std::any_of . A versão OpenMP esse exemplo usa um sinalizador para coordenar entre todas as iterações paralelas loop for que a condição esteve atendida. A versão que usa o tempo de execução de simultaneidade usa o método de concurrency::structured_task_group::cancel para interromper a operação total quando a condição for atendida.
// 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;
}
}
O exemplo produz a seguinte saída.
Na versão do usa OpenMP, todas as interações do loop é executado, mesmo quando o sinalizador é definido. Além disso, se uma tarefa teve ter quaisquer tarefas filhos, o sinalizador também deveriam estar disponível 2 essas tarefas filhos comunicar o cancelamento. Em tempo de execução da simultaneidade, quando um grupo de trabalho for cancelado, o tempo de execução cancela toda a árvore de trabalho, incluindo tarefas filhos. As tarefas do usa o algoritmo de concurrency::parallel_for_each executar o trabalho. Em virtude disso, quando uma iteração do loop cancela a tarefa raiz, a árvore inteira de computação é cancelada também. Quando uma árvore de trabalho for cancelada, o tempo de execução não inicia novas tarefas. No entanto, o tempo de execução permite que as tarefas que têm iniciado já para concluir. Em virtude disso, no caso do algoritmo de parallel_for_each , as iterações ativos de loop podem limpar seus recursos.
Em ambas as versões desse exemplo, se a matriz contém mais de uma cópia de valor para pesquisar por, várias interações do loop abrange cada um definem simultaneamente o resultado e cancelar a operação total. Você pode usar um primitivo de sincronização, como uma seção crítica, se seu problema que requer apenas uma tarefa executa o trabalho quando uma condição for atendida.
Você também pode usar a manipulação de exceção para cancelar as tarefas que usam o PPL. Para obter mais informações sobre cancelamento, consulte Cancelamento no PPL.
Para obter mais informações sobre como parallel_for_each e outros algoritmos paralelos, consulte Algoritmos paralelos.
Compilando o código
Copie o código de exemplo e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado concrt-omp-parallel-any-of.cpp e execute o comando a seguir em uma janela de prompt de comando do Visual Studio.
cl.exe /EHsc /openmp concrt-omp-parallel-any-of.cpp