Compartir a través de


Cómo: Usar la clase combinable para mejorar el rendimiento

En este ejemplo se muestra cómo usar la clase concurrency::combinable para calcular la suma de los números de un objeto std::array que son primos. La clase combinable mejora el rendimiento eliminando el estado compartido.

Sugerencia

En algunos casos, la asignación en paralelo (concurrency::parallel_transform) y la reducción (concurrency:: parallel_reduce) pueden proporcionar mejoras de rendimiento respecto a combinable. Puede ver un ejemplo que utiliza las operaciones de asignación y reducción para producir los mismos resultados que en este ejemplo en Algoritmos paralelos.

Ejemplo: acumulación

En el siguiente ejemplo se usa la función std::accumulate para calcular la suma de los elementos de una matriz que son primos. En este ejemplo, a es un objeto array y la función is_prime determina si su valor de entrada es primo.

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

Ejemplo: parallel_for_each

En el siguiente ejemplo se muestra una manera sencilla de ejecutar el ejemplo anterior en paralelo. En este ejemplo se usa el algoritmo concurrency::parallel_for_each para procesar la matriz en paralelo y un objeto concurrency::critical_section para sincronizar el acceso a la variable prime_sum. Este ejemplo no se escala porque cada subproceso debe esperar a que el recurso compartido esté disponible.

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

Ejemplo: combinable

En el siguiente ejemplo se usa un objeto combinable para mejorar el rendimiento del ejemplo anterior. En este ejemplo se elimina la necesidad de usar los objetos de sincronización; se escala porque el objeto combinable permite a cada subproceso realizar su tarea independientemente.

Un objeto combinable se usa normalmente en dos pasos. Primero, genere una serie de cálculos específicos realizando el trabajo en paralelo. Luego, combine (o reduzca) los cálculos en un resultado final. En este ejemplo se usa el método concurrency::combinable::local para obtener una referencia a la suma local. A continuación, se usa el método concurrency::combinable::combine y un objeto std::plus para combinar los cálculos locales en el resultado final.

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

Ejemplo: serie y paralelo

El siguiente ejemplo completo calcula la suma de números primos consecutivamente y en paralelo. El ejemplo imprime en la consola el tiempo necesario para realizar ambos cálculos.

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

La siguiente salida de ejemplo corresponde a un equipo con cuatro procesadores.

1709600813
serial time: 6178 ms

1709600813
parallel time: 1638 ms

Compilar el código

Para compilar el código, cópielo y péguelo en un proyecto de Visual Studio, o en un archivo denominado parallel-sum-of-primes.cpp. Después, ejecute el comando siguiente en una ventana del símbolo del sistema de Visual Studio.

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

Programación sólida

Para consultar un ejemplo que usa las operaciones de asignación y reducción para producir los mismos resultados, vea Algoritmos paralelos.

Consulte también

Contenedores y objetos paralelos
combinable (clase)
critical_section (clase)