Compartilhar via


Como usar contêineres em paralelo para aumentar a eficiência

Este tópico mostra como usar o contêiner paralelos a eficiente armazenar e acessar dados em paralelo.

O código de exemplo calcula o conjunto de aspas e os números de Carmichael em paralelo. Então, para cada número de Carmichael, o código a seguir calcula os fatores principais do número.

Exemplo

O exemplo a seguir mostra a função de is_prime , que determina se o valor de entrada é um número principal, e a função de is_carmichael , que determina se o valor de entrada é um número de Carmichael.

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

// Determines whether the input value is a Carmichael number. 
bool is_carmichael(const int n) 
{
   if (n < 2) 
      return false;

   int k = n;
   for (int i = 2; i <= k / i; ++i) 
   {
      if (k % i == 0) 
      {
         if ((k / i) % i == 0) 
            return false;
         if ((n - 1) % (i - 1) != 0)
            return false;
         k /= i;
         i = 1;
      }
   }
   return k != n && (n - 1) % (k - 1) == 0;
}

O exemplo a seguir usa is_prime e is_carmichael funções para computar os conjuntos de aspas e os números de Carmichael. O exemplo usa algoritmos de concurrency::parallel_invoke e de concurrency::parallel_for para computar em paralelo cada cluster. Para obter mais informações sobre algoritmos paralelos, consulte Algoritmos paralelos.

Este exemplo usa um objeto de concurrency::concurrent_queue para manter o conjunto de números de Carmichael usará esse objeto porque posteriormente como uma fila de trabalho. Usa um objeto de concurrency::concurrent_vector para manter o conjunto de números à esquerda como iterará posteriormente por esse conjunto para localizar fatores principais.

// The maximum number to test. 
const int max = 10000000;

// Holds the Carmichael numbers that are in the range [0, max).
concurrent_queue<int> carmichaels;

// Holds the prime numbers that are in the range  [0, sqrt(max)).
concurrent_vector<int> primes;

// Generate the set of Carmichael numbers and the set of prime numbers 
// in parallel.
parallel_invoke(
   [&] {
      parallel_for(0, max, [&](int i) {
         if (is_carmichael(i)) {
            carmichaels.push(i);
         }
      });
   },
   [&] {
      parallel_for(0, int(sqrt(static_cast<double>(max))), [&](int i) {
         if (is_prime(i)) {
            primes.push_back(i);
         }
      });
   });

O exemplo a seguir mostra a função de prime_factors_of , que usa a divisão de avaliação para localizar todos os fatores principais do valor fornecido.

Essa função usa o algoritmo de concurrency::parallel_for_each para iteração pela coleção de números à esquerda. O objeto de concurrent_vector habilita o loop paralelo para adicionar simultaneamente fatores principais ao resultado.

// Finds all prime factors of the given value.
concurrent_vector<int> prime_factors_of(int n, 
   const concurrent_vector<int>& primes)
{
   // Holds the prime factors of n.
   concurrent_vector<int> prime_factors;

   // Use trial division to find the prime factors of n. 
   // Every prime number that divides evenly into n is a prime factor of n. 
   const int max = sqrt(static_cast<double>(n));
   parallel_for_each(begin(primes), end(primes), [&](int prime)
   {
      if (prime <= max)
      {         
         if ((n % prime) == 0)
            prime_factors.push_back(prime);
      }
   });

   return prime_factors;
}

Este exemplo processa cada elemento na fila de números de Carmichael chamando a função de prime_factors_of para computar os fatores principais. Usa um grupo de trabalho seja executada neste trabalho. Para obter mais informações sobre grupos de trabalho, consulte Paralelismo de tarefa (tempo de execução de simultaneidade).

Este exemplo a seguir imprime os fatores principais para cada número de Carmichael se o número tiver mais de quatro fatores principais.

// Use a task group to compute the prime factors of each  
// Carmichael number in parallel.
task_group tasks;

int carmichael;
while (carmichaels.try_pop(carmichael))
{
   tasks.run([carmichael,&primes] 
   {
      // Compute the prime factors.
      auto prime_factors = prime_factors_of(carmichael, primes);

      // For brevity, print the prime factors for the current number only 
      // if there are more than 4. 
      if (prime_factors.size() > 4)
      {
         // Sort and then print the prime factors.
         sort(begin(prime_factors), end(prime_factors));

         wstringstream ss;
         ss << L"Prime factors of " << carmichael << L" are:";

         for_each (begin(prime_factors), end(prime_factors), 
            [&](int prime_factor) { ss << L' ' << prime_factor; });
         ss << L'.' << endl;

         wcout << ss.str();
      }
   });
}

// Wait for the task group to finish.
tasks.wait();

O código a seguir mostra o exemplo completo, que usa contêiner paralelos para computar os fatores principais dos números de Carmichael.

// carmichael-primes.cpp 
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_queue.h>
#include <concurrent_vector.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

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

// Determines whether the input value is a Carmichael number. 
bool is_carmichael(const int n) 
{
   if (n < 2) 
      return false;

   int k = n;
   for (int i = 2; i <= k / i; ++i) 
   {
      if (k % i == 0) 
      {
         if ((k / i) % i == 0) 
            return false;
         if ((n - 1) % (i - 1) != 0)
            return false;
         k /= i;
         i = 1;
      }
   }
   return k != n && (n - 1) % (k - 1) == 0;
}

// Finds all prime factors of the given value.
concurrent_vector<int> prime_factors_of(int n, 
   const concurrent_vector<int>& primes)
{
   // Holds the prime factors of n.
   concurrent_vector<int> prime_factors;

   // Use trial division to find the prime factors of n. 
   // Every prime number that divides evenly into n is a prime factor of n. 
   const int max = sqrt(static_cast<double>(n));
   parallel_for_each(begin(primes), end(primes), [&](int prime)
   {
      if (prime <= max)
      {         
         if ((n % prime) == 0)
            prime_factors.push_back(prime);
      }
   });

   return prime_factors;
}

int wmain()
{
   // The maximum number to test. 
   const int max = 10000000;

   // Holds the Carmichael numbers that are in the range [0, max).
   concurrent_queue<int> carmichaels;

   // Holds the prime numbers that are in the range  [0, sqrt(max)).
   concurrent_vector<int> primes;

   // Generate the set of Carmichael numbers and the set of prime numbers 
   // in parallel.
   parallel_invoke(
      [&] {
         parallel_for(0, max, [&](int i) {
            if (is_carmichael(i)) {
               carmichaels.push(i);
            }
         });
      },
      [&] {
         parallel_for(0, int(sqrt(static_cast<double>(max))), [&](int i) {
            if (is_prime(i)) {
               primes.push_back(i);
            }
         });
      });

   // Use a task group to compute the prime factors of each  
   // Carmichael number in parallel.
   task_group tasks;

   int carmichael;
   while (carmichaels.try_pop(carmichael))
   {
      tasks.run([carmichael,&primes] 
      {
         // Compute the prime factors.
         auto prime_factors = prime_factors_of(carmichael, primes);

         // For brevity, print the prime factors for the current number only 
         // if there are more than 4. 
         if (prime_factors.size() > 4)
         {
            // Sort and then print the prime factors.
            sort(begin(prime_factors), end(prime_factors));

            wstringstream ss;
            ss << L"Prime factors of " << carmichael << L" are:";

            for_each (begin(prime_factors), end(prime_factors), 
               [&](int prime_factor) { ss << L' ' << prime_factor; });
            ss << L'.' << endl;

            wcout << ss.str();
         }
      });
   }

   // Wait for the task group to finish.
   tasks.wait();
}

Esse exemplo gera a seguinte saída de amostra.

  

Compilando o código

Copie o código de exemplo e cole-o em um projeto do Visual Studio, ou cole-o em um arquivo chamado carmichael-primes.cpp e execute o comando a seguir em uma janela de prompt de comando do Visual Studio.

cl.exe /EHsc carmichael-primes.cpp

Consulte também

Referência

Classe concurrent_vector

Classe concurrent_queue

Função parallel_invoke

Função parallel_for

Classe task_group

Conceitos

Contêineres e objetos em paralelo

Paralelismo de tarefa (tempo de execução de simultaneidade)