Vorgehensweise: Schreiben einer parallel_for_each
Schleife
In diesem Beispiel wird gezeigt, wie der concurrency::parallel_for_each
Algorithmus verwendet wird, um die Anzahl der Primzahlen in einem std::array
Objekt parallel zu berechnen.
Beispiel
Im folgenden Beispiel wird die Anzahl von Primzahlen in einem Array zweimal berechnet. Im Beispiel wird zunächst der std::for_each
Algorithmus verwendet, um die Anzahl fortlaufend zu berechnen. Anschließend wird die gleiche Aufgabe mit dem parallel_for_each
-Algorithmus parallel ausgeführt. Das Beispiel gibt außerdem die Zeit, die zum Ausführen beider Berechnungen benötigt wird, in der Konsole aus.
// parallel-count-primes.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <iostream>
#include <algorithm>
#include <array>
using namespace concurrency;
using namespace std;
// Returns the number of milliseconds that it takes to call the passed in function.
template <class Function>
__int64 time_call(Function&& f)
{
__int64 begin = GetTickCount();
f();
return GetTickCount() - begin;
}
// Determines whether the input is a prime.
bool is_prime(int n)
{
if (n < 2)
{
return false;
}
for (int i = 2; i < int(std::sqrt(n)) + 1; ++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(begin(a), end(a), [&]
{
return n++;
});
// Use the for_each algorithm to count, serially, the number
// of prime numbers in the array.
LONG prime_count = 0L;
__int64 elapsed = time_call([&]
{
for_each(begin(a), end(a), [&](int n)
{
if (is_prime(n))
{
++prime_count;
}
});
});
wcout << L"serial version: " << endl
<< L"found " << prime_count << L" prime numbers" << endl
<< L"took " << elapsed << L" ms" << endl << endl;
// Use the parallel_for_each algorithm to count, in parallel, the number
// of prime numbers in the array.
prime_count = 0L;
elapsed = time_call([&]
{
parallel_for_each(begin(a), end(a), [&](int n)
{
if (is_prime(n))
{
InterlockedIncrement(&prime_count);
}
});
});
wcout << L"parallel version: " << endl
<< L"found " << prime_count << L" prime numbers" << endl
<< L"took " << elapsed << L" ms" << endl << endl;
}
Die folgende Beispielausgabe ist für einen Computer mit vier Kernen vorgesehen.
serial version:
found 17984 prime numbers
took 125 ms
parallel version:
found 17984 prime numbers
took 63 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-count-primes.cpp
ist, und führen Sie dann den folgenden Befehl in einem Visual Studio-Eingabeaufforderungsfenster aus.
cl.exe /EHsc parallel-count-primes.cpp
Stabile Programmierung
Der Lambdaausdruck, der im Beispiel an den parallel_for_each
-Algorithmus übergeben wird, aktiviert mithilfe der InterlockedIncrement
-Funktion parallele Iterationen der Schleife, um den Zähler gleichzeitig zu inkrementieren. Wenn Sie Funktionen wie z. B. InterlockedIncrement
verwenden, um Zugriff auf freigegebene Ressourcen zu synchronisieren, kann es zu Leistungsengpässen im Code kommen. Sie können einen sperrfreien Synchronisierungsmechanismus verwenden, z. B. die concurrency::combinable
Klasse, um gleichzeitigen Zugriff auf freigegebene Ressourcen zu vermeiden. Ein Beispiel, das die combinable
Klasse auf diese Weise verwendet, finden Sie unter How to: Use combinable to improve performance.