Comment : convertir une boucle OpenMP qui a recours à l’annulation pour utiliser le runtime d’accès concurrentiel
Certaines boucles parallèles ne nécessitent pas que toutes les itérations soient exécutées. Par exemple, un algorithme qui recherche une valeur peut se terminer une fois la valeur trouvée. OpenMP ne fournit pas de mécanisme permettant de sortir d’une boucle parallèle. Toutefois, vous pouvez utiliser une valeur booléenne ou un indicateur pour activer une itération de la boucle pour indiquer que la solution a été trouvée. Le runtime d’accès concurrentiel fournit des fonctionnalités qui permettent à une tâche d’annuler d’autres tâches qui n’ont pas encore démarré.
Cet exemple montre comment convertir un parallèleOpenMP pour la boucle qui ne nécessite pas que toutes les itérations s’exécutent pour utiliser le mécanisme d’annulation du runtime d’accès concurrentiel.
Exemple
Cet exemple utilise OpenMP et le runtime d’accès concurrentiel pour implémenter une version parallèle de l’algorithme std ::any_of . La version OpenMP de cet exemple utilise un indicateur pour coordonner entre toutes les itérations de boucle parallèle que la condition a été remplie. La version qui utilise le runtime d’accès concurrentiel utilise la méthode concurrency ::structured_task_group ::cancel pour arrêter l’opération globale lorsque la condition est remplie.
// 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;
}
}
Cet exemple produit la sortie suivante.
Using OpenMP...
9114046 is in the array.
Using the Concurrency Runtime...
9114046 is in the array.
Dans la version de ce paramètre utilise OpenMP, toutes les itérations de la boucle s’exécutent, même lorsque l’indicateur est défini. En outre, si une tâche devait avoir des tâches enfants, l’indicateur doit également être disponible pour ces tâches enfants pour communiquer l’annulation. Dans le runtime d’accès concurrentiel, lorsqu’un groupe de tâches est annulé, le runtime annule toute l’arborescence du travail, y compris les tâches enfants. L’accès concurrentiel ::p arallel_for_each utilise des tâches pour effectuer des tâches. Par conséquent, lorsqu’une itération de la boucle annule la tâche racine, l’arborescence entière du calcul est également annulée. Lorsqu’une arborescence de travail est annulée, le runtime ne démarre pas de nouvelles tâches. Toutefois, le runtime permet aux tâches qui ont déjà commencé à se terminer. Par conséquent, dans le cas de l’algorithme parallel_for_each
, les itérations de boucles actives peuvent nettoyer leurs ressources.
Dans les deux versions de cet exemple, si le tableau contient plusieurs copies de la valeur à rechercher, plusieurs itérations de boucle peuvent chacune définir simultanément le résultat et annuler l’opération globale. Vous pouvez utiliser une primitive de synchronisation, telle qu’une section critique, si votre problème nécessite qu’une seule tâche effectue un travail lorsqu’une condition est remplie.
Vous pouvez également utiliser la gestion des exceptions pour annuler les tâches qui utilisent la bibliothèque PPL. Pour plus d’informations sur l’annulation, consultez Annulation dans la bibliothèque PPL.
Pour plus d’informations sur parallel_for_each
et d’autres algorithmes parallèles, consultez Algorithmes parallèles.
Compilation du code
Copiez l’exemple de code et collez-le dans un projet Visual Studio, ou collez-le dans un fichier nommé concrt-omp-parallel-any-of.cpp
, puis exécutez la commande suivante dans une fenêtre d’invite de commandes Visual Studio.
cl.exe /EHsc /openmp concrt-omp-parallel-any-of.cpp
Voir aussi
Migration d’OpenMP au runtime d’accès concurrentiel
Annulation dans la bibliothèque de modèles parallèles
Algorithmes parallèles