Gewusst wie: Verbessern der Leistung mithilfe von combinable

In diesem Beispiel wird gezeigt, wie die Concurrency::combinable-Klasse verwendet wird, um die Summe der Zahlen in einem std::array-Objekt zu berechnen, bei denen es sich um Primzahlen handelt. Die combinable-Klasse steigert die Leistung, indem sie Freigabezustand ausschließt.

Beispiel

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

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

Das folgende Beispiel zeigt einen naiven Weg, das vorherige Beispiel zu parallelisieren. In diesem Beispiel wird der Concurrency::parallel_for_each-Algorithmus zum parallelen Verarbeiten des Arrays und ein Concurrency::critical_section-Objekt zum Synchronisieren des Zugriffs auf die prime_sum-Variable verwendet. 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(a.begin(), a.end(), [&](int i) {
   cs.lock();
   prime_sum += (is_prime(i) ? i : 0);
   cs.unlock();
});

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 Concurrency::combinable::local-Methode verwendet, um einen Verweis auf die lokale Summe zu erhalten. Anschließend werden mit der Concurrency::combinable::combine-Methode und einem std::plus-Objekt die lokalen Berechnungen zum Endergebnis zusammengefasst.

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

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.
   int n = 0;
   generate(a.begin(), a.end(), [&] {
      return n++;
   });

   int prime_sum;
   __int64 elapsed;

   // Compute the sum of the numbers in the array that are prime.
   elapsed = time_call([&] {
      prime_sum = accumulate(a.begin(), a.end(), 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(a.begin(), a.end(), [&](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

Kopieren Sie den Code zum Kompilieren, und fügen Sie ihn in ein Visual Studio-Projekt ein. Alternativ dazu können Sie ihn in eine Datei mit dem Namen parallel-sum-of-primes.cpp einfügen und dann folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster ausführen.

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

Siehe auch

Referenz

combinable-Klasse

critical_section-Klasse

Konzepte

Parallele Container und Objekte