Dela via


Gör så här: Använd kombinerbart för att förbättra prestanda

Det här exemplet visar hur du använder klassen concurrency::combinable för att beräkna summan av talen i ett std::array-objekt som är primtal. Klassen combinable förbättrar prestandan genom att eliminera delat tillstånd.

Tips/Råd

I vissa fall kan parallell kartläggning (konkurrens::parallel_transform) och reducera (konkurrens::parallel_reduce) ge prestandaförbättringar över combinable. Ett exempel som använder map- och reduce-operationer för att generera samma resultat som i det här exemplet finns i Parallella algoritmer.

Exempel – ackumulera

I följande exempel används funktionen std::accumulate för att beräkna summan av elementen i en matris som är primär. I det här exemplet a är ett array objekt och is_prime funktionen avgör om dess indatavärde är primärt.

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

Exempel – parallel_for_each

I följande exempel visas ett naivt sätt att parallellisera föregående exempel. I det här exemplet används concurrency::parallel_for_each-algoritmen för att bearbeta matrisen parallellt och ett concurrency::critical_section-objekt för att synkronisera åtkomsten till variabeln prime_sum. Det här exemplet skalas inte eftersom varje tråd måste vänta tills den delade resursen blir tillgänglig.

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();
});

Exempel – kombinerbart

I följande exempel används ett combinable objekt för att förbättra prestandan i föregående exempel. Det här exemplet eliminerar behovet av synkroniseringsobjekt. den skalas eftersom objektet combinable gör det möjligt för varje tråd att utföra sin uppgift oberoende av varandra.

Ett combinable objekt används vanligtvis i två steg. Skapa först en serie detaljerade beräkningar genom att utföra arbete parallellt. Kombinera sedan (eller minska) beräkningarna till ett slutligt resultat. I det här exemplet används metoden concurrency::combinable::local för att hämta en referens till den lokala summan. Den använder sedan metoden concurrency::combinable::combine och ett std::plus-objekt för att slå samman de lokala beräkningarna till det slutliga resultatet.

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>());

Exempel – seriell och parallell

I följande fullständiga exempel beräknas summan av primtal både seriellt och parallellt. Exemplet skriver ut till konsolen den tid som krävs för att utföra båda beräkningarna.

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

Följande exempelutdata är för en dator som har fyra processorer.

1709600813
serial time: 6178 ms

1709600813
parallel time: 1638 ms

Kompilera koden

Om du vill kompilera koden kopierar du den och klistrar sedan in den i ett Visual Studio-projekt eller klistrar in den i en fil med namnet parallel-sum-of-primes.cpp och kör sedan följande kommando i ett Visual Studio-kommandotolkfönster.

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

Robust Programmering

Ett exempel som använder map- och reduce-operationer för att producera samma resultat finns i Parallella algoritmer.

Se även

parallella containrar och objekt
kombinerbar klass
kritisk_sektion-klass