Comment : utiliser des conteneurs parallèles pour une efficacité accrue
Cette rubrique indique comment utiliser des conteneurs parallèles pour stocker et accéder efficacement à des données en parallèle.
L'exemple de code calcule l'ensemble des nombres premiers et des nombres de Carmichael en parallèle. Ensuite, pour chaque nombre de Carmichael, le code calcule les facteurs premiers de ce nombre.
Exemple
L'exemple suivant illustre la fonction is_prime, qui détermine si une valeur d'entrée est un nombre premier, et la fonction is_carmichael, qui détermine si la valeur d'entrée est un nombre 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;
}
L'exemple suivant utilise les fonctions is_carmichael et is_prime pour calculer les ensembles de nombres premiers et de nombres de Carmichael. L'exemple utilise les algorithmes concurrency::parallel_invoke et concurrency::parallel_for pour calculer chaque ensemble en parallèle. Pour plus d'informations sur les algorithmes parallèles, consultez Algorithmes parallèles.
Cet exemple utilise un objet concurrency::concurrent_queue pour stocker l'ensemble des nombres de Carmichael, car il utilisera ultérieurement cet objet comme file d'attente de travail. Il utilise un objet concurrency::concurrent_vector pour stocker l'ensemble de nombres premiers, car il itérera ultérieurement au sein de cet ensemble pour rechercher les facteurs premiers.
// 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);
}
});
});
L'exemple suivant illustre la fonction prime_factors_of, qui utilise la division par évaluation pour rechercher tous les facteurs premiers de la valeur donnée.
Cette fonction utilise l'algorithme concurrency::parallel_for_each pour itérer au sein de la collection de nombres premiers. L'objet concurrent_vector permet à la boucle parallèle d'ajouter simultanément des facteurs premiers au résultat.
// 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;
}
Cet exemple traite chaque élément dans la file d'attente de nombres de Carmichael en appelant la fonction prime_factors_of pour calculer ses facteurs premiers. Il utilise un groupe de tâches pour effectuer ce travail en parallèle. Pour plus d'informations sur les groupes de tâches, consultez Parallélisme des tâches (runtime d'accès concurrentiel).
Cet exemple imprime les facteurs premiers pour chaque nombre de Carmichael si ce nombre possède plus de quatre facteurs premiers.
// 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();
Le code suivant montre l'exemple complet, qui utilise des conteneurs parallèles pour calculer les facteurs premiers des nombres 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();
}
Cet exemple génère l'exemple de sortie suivant.
Compilation du code
Copiez l'exemple de code et collez-le dans un projet Visual Studio , ou collez-le dans un fichier nommé carmichael-primes.cpp puis exécutez la commande suivante dans une fenêtre d'invite de commandes Visual Studio.
cl.exe /EHsc carmichael-primes.cpp