Gewusst wie: Verbessern der Leistung mithilfe von combinable

In diesem Beispiel wird gezeigt, wie Sie die Parallelitätsklasse::kombinationsfähige Klasse verwenden, um die Summe der Zahlen in einem std::array-Objekt zu berechnen, das primiert ist. Die combinable-Klasse steigert die Leistung, indem sie Freigabezustand ausschließt.

Tipp

In einigen Fällen kann parallele Zuordnung (parallele Zuordnung::p arallel_transform) und reduzierung (Parallelität:: parallel_reduce) Leistungsverbesserungen combinablebieten. Ein Beispiel, das Zuordnungen verwendet und Vorgänge reduziert, um dieselben Ergebnisse wie in diesem Beispiel zu erzielen, finden Sie unter Parallel-Algorithmen.

Beispiel : Akkumulation

Im folgenden Beispiel wird die Funktion "std::accumulate " verwendet, um die Summe der Elemente in einem Array zu berechnen, das primiert ist. In diesem Beispiel ist a ein array-Objekt, und die is_prime-Funktion bestimmt, ob sein Eingabewert eine Primzahl ist.

prime_sum = accumulate(begin(a), end(a), 0, [&](int acc, int i) {
   return acc + (is_prime(i) ? i : 0);
});

Beispiel – parallel_for_each

Das folgende Beispiel zeigt einen naiven Weg, das vorherige Beispiel zu parallelisieren. In diesem Beispiel wird der Parallelitätsalgorithmus::p arallel_for_each verwendet, um das Array parallel und ein Parallelitätsobjekt::critical_section objekt zum Synchronisieren des Zugriffs auf die prime_sum Variable zu verarbeiten. Dieses Beispiel skaliert nicht, da jeder Thread warten muss, bis die freigegebene Ressource verfügbar wird.

critical_section cs;
prime_sum = 0;
parallel_for_each(begin(a), end(a), [&](int i) {
   cs.lock();
   prime_sum += (is_prime(i) ? i : 0);
   cs.unlock();
});

Beispiel - kombinierbar

Im folgenden Beispiel wird ein combinable-Objekt zum Steigern der Leistung des vorherigen Beispiels verwendet. In diesem Beispiel sind keine Synchronisierungsobjekte erforderlich. Es skaliert, da das combinable-Objekt dafür sorgt, dass jeder Thread seine Aufgabe unabhängig ausführt.

Ein combinable-Objekt wird in der Regel in zwei Schritten verwendet. Erzeugen Sie zuerst eine Reihe von differenzierten Berechnungen, indem Sie Arbeiten parallel ausführen. Fassen Sie danach die Berechnungen in einem Endergebnis zusammen (oder reduzieren Sie sie). In diesem Beispiel wird die Parallelität::combinable::local-Methode verwendet, um einen Verweis auf die lokale Summe abzurufen. Anschließend wird die Parallelität::combinable::combine-Methode und ein std::p lus-Objekt verwendet, um die lokalen Berechnungen mit dem Endergebnis zu kombinieren.

combinable<int> sum;
parallel_for_each(begin(a), end(a), [&](int i) {
   sum.local() += (is_prime(i) ? i : 0);
});
prime_sum = sum.combine(plus<int>());

Beispiel – seriell und parallel

Im folgenden vollständigen Beispiel wird die Summe von Primzahlen sowohl seriell als auch parallel berechnet. Das Beispiel gibt die Zeit, die zum Ausführen beider Berechnungen benötigt wird, in der Konsole aus.

// parallel-sum-of-primes.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <array>
#include <numeric>
#include <iostream>

using namespace concurrency;
using namespace std;

// Calls the provided work function and returns the number of milliseconds 
// that it takes to call that function.
template <class Function>
__int64 time_call(Function&& f)
{
   __int64 begin = GetTickCount();
   f();
   return GetTickCount() - begin;
}

// Determines whether the input value is prime.
bool is_prime(int n)
{
   if (n < 2)
      return false;
   for (int i = 2; i < n; ++i)
   {
      if ((n % i) == 0)
         return false;
   }
   return true;
}

int wmain()
{   
   // Create an array object that contains 200000 integers.
   array<int, 200000> a;

   // Initialize the array such that a[i] == i.
   iota(begin(a), end(a), 0);

   int prime_sum;
   __int64 elapsed;

   // Compute the sum of the numbers in the array that are prime.
   elapsed = time_call([&] {
      prime_sum = accumulate(begin(a), end(a), 0, [&](int acc, int i) {
         return acc + (is_prime(i) ? i : 0);
      });
   });   
   wcout << prime_sum << endl;   
   wcout << L"serial time: " << elapsed << L" ms" << endl << endl;

   // Now perform the same task in parallel.
   elapsed = time_call([&] {
      combinable<int> sum;
      parallel_for_each(begin(a), end(a), [&](int i) {
         sum.local() += (is_prime(i) ? i : 0);
      });
      prime_sum = sum.combine(plus<int>());
   });
   wcout << prime_sum << endl;
   wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;
}

Die folgende Beispielausgabe entspricht einem Ergebnis auf einem Computer mit vier Prozessoren.

1709600813
serial time: 6178 ms

1709600813
parallel time: 1638 ms

Kompilieren des Codes

Um den Code zu kompilieren, kopieren Sie ihn, und fügen Sie ihn dann in ein Visual Studio-Projekt ein, oder fügen Sie ihn in eine Datei ein, die benannt parallel-sum-of-primes.cpp ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.

cl.exe /EHsc parallel-sum-of-primes.cpp

Stabile Programmierung

Ein Beispiel, das Zuordnungen verwendet und Vorgänge reduziert, um dieselben Ergebnisse zu erzielen, finden Sie unter Parallel-Algorithmen.

Siehe auch

Parallele Container und Objekte
combinable-Klasse
critical_section-Klasse