Partager via


Comment : convertir une boucle OpenMP qui a recours à l'annulation pour utiliser le runtime d'accès concurrentiel

Certaines boucles parallèles ne requièrent pas l'exécution de toutes les itérations. Par exemple, un algorithme qui recherche une valeur peut se terminer lorsque la valeur est trouvée. OpenMP ne fournit pas de mécanisme pour quitter une boucle parallèle. Toutefois, vous pouvez utiliser une valeur booléenne, ou indicateur, pour permettre à une itération de la boucle d'indiquer que la solution a été trouvée. Le runtime d'accès concurrentiel fournit des fonctionnalités qui activent une tâche pour annuler d'autres tâches qui n'ont pas encore démarré.

Cet exemple montre comment convertir une boucle parallel for OpenMP qui ne requiert pas l'exécution de toutes les itérations pour que le mécanisme d'annulation du runtime d'accès concurrentiel puisse être utilisé.

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 toutes les itérations de boucle parallèle remplies par la condition. 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 génère la sortie suivante.

  

Dans la version qui utilise OpenMP, toutes les itérations de la boucle s'exécutent, même quand l'indicateur est défini. En outre, si une tâche comporte des tâches enfants, l'indicateur doit également être disponible pour ces tâches enfants afin de communiquer l'annulation. Dans le runtime d'accès concurrentiel, quand un groupe de tâches est annulé, le runtime annule l'ensemble de l'arborescence de travail, y compris les tâches enfants. L'algorithme concurrency::parallel_for_each utilise des tâches pour exécuter un travail. Par conséquent, si une itération de la boucle annule la tâche racine, l'ensemble de l'arborescence du calcul est également annulé. Lorsqu'une arborescence de travail est annulée, le runtime ne démarre pas de nouvelle tâche. Toutefois, le runtime permet aux tâches qui ont déjà démarré de 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 définir simultanément le résultat et annuler l'opération globale. Vous pouvez utiliser une primitive de synchronisation, tel 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 des tâches qui utilisent la bibliothèque PPL. Pour plus d'informations sur l'annulation, consultez Annulation dans la bibliothèque de modèles parallèles.

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

Concepts

Migration d'OpenMP au runtime d'accès concurrentiel

Annulation dans la bibliothèque de modèles parallèles

Algorithmes parallèles