Freigeben über


Gewusst wie: Konvertieren einer OpenMP-Schleife mit Abbruch zur Verwendung der Concurrency Runtime

Bei einigen parallelen Schleifen müssen nicht alle Iterationen ausgeführt werden. Beispielsweise kann ein Algorithmus, mit dem nach einem Wert gesucht wird, beendet werden, sobald der Wert gefunden wurde. OpenMP stellt keinen Mechanismus bereit, um aus einer parallelen Schleife auszubrechen. Sie können jedoch einen booleschen Wert oder ein Flag verwenden, damit die Iteration einer Schleife angeben kann, dass der gesuchte Wert gefunden wurde. Die Concurrency Runtime stellt Funktionen bereit, mit denen eine Aufgabe andere Aufgaben abbrechen kann, die noch nicht gestartet wurden.

In diesem Beispiel wird veranschaulicht, wie Sie eine OpenMP-Parallele für Schleife konvertieren, die nicht für alle Iterationen ausgeführt werden muss, um den Abbruchmechanismus für parallele Runtime zu verwenden.

Beispiel

In diesem Beispiel wird sowohl OpenMP als auch die Parallelitätslaufzeit verwendet, um eine parallele Version des Algorithmus "std::any_of " zu implementieren. In der OpenMP-Version in diesem Beispiel wird mit einem Flag die Erfüllung der Bedingung für alle parallelen Schleifeniterationen koordiniert. Die Version, die die Concurrency Runtime verwendet, verwendet die Concurrency::structured_task_group::cancel-Methode , um den Gesamtvorgang zu beenden, wenn die Bedingung erfüllt ist.

// 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;
   }
}

Folgende Ergebnisse werden zurückgegeben:

Using OpenMP...
9114046 is in the array.
Using the Concurrency Runtime...
9114046 is in the array.

In der Version mit OpenMP werden, auch wenn das Flag festgelegt ist, alle Schleifeniterationen ausgeführt. Bei einer Aufgabe mit untergeordneten Aufgaben müsste das Flag darüber hinaus auch für diese verfügbar sein, um den Abbruch des Vorgangs zu signalisieren. Wenn eine Aufgabengruppe abgebrochen wird, bricht die Laufzeit in der Parallelitätslaufzeit die gesamte Arbeitsstruktur ab, einschließlich untergeordneter Aufgaben. Die Parallelität::p arallel_for_each Algorithmus verwendet Aufgaben, um Arbeit auszuführen. Wenn daher eine Iteration der Schleife die Stammaufgabe abbricht, wird auch die gesamte Struktur der Berechnung abgebrochen. Wenn eine Arbeitsstruktur abgebrochen wird, startet die Laufzeit keine neuen Aufgaben. Aufgaben, die bereits gestartet wurden, wird von der Laufzeit jedoch eine Fertigstellung ermöglicht. Aktive Schleifeniterationen können im Falle des parallel_for_each-Algorithmus also ihre Ressourcen bereinigen.

Im Beispiel kann das Ergebnis in beiden Versionen durch mehrere Schleifeniterationen parallel festgelegt und der Gesamtvorgang kann abgebrochen werden, wenn das Array mehr als eine Kopie des gesuchten Werts enthält. Mit einem Synchronisierungsprimitiven wie einem kritischen Abschnitt können Sie ggf. sicherstellen, dass Arbeiten nur von einer Aufgabe ausgeführt werden, wenn eine Bedingung erfüllt ist.

Sie können Aufgaben mit der PPL auch mit der Ausnahmebehandlung abbrechen. Weitere Informationen zum Abbruch finden Sie unter "Abbruch" in der PPL.

Weitere Informationen zu parallel_for_each und anderen parallelen Algorithmen finden Sie unter Parallel-Algorithmen.

Kompilieren des Codes

Kopieren Sie den Beispielcode, fügen Sie ihn in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt concrt-omp-parallel-any-of.cpp ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.

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

Siehe auch

Migrieren von OpenMP zur Concurrency Runtime
Abbruch in der PPL
Parallele Algorithmen