introduction
Bonjour, Habr!
Selon mes normes, j'écris du code C ++ depuis assez longtemps, mais jusqu'à ce moment-là, je n'avais pas encore rencontré de tâches liées au calcul parallèle. Je n'ai pas vu un seul article sur la bibliothèque Boost.Compute, donc cet article en parlera.
Toutes les parties
- Partie 1
- Partie 2
Contenu
- Qu'est-ce que boost.compute
- Problèmes de connexion de boost.compute au projet
- Introduction à boost.compute
- Classes de calcul de base
- Commencer
- Conclusion
Qu'est-ce que boost.compute
Cette bibliothèque c ++ fournit une interface simple de haut niveau pour interagir avec des processeurs multi-cœurs et des périphériques informatiques GPU. Cette bibliothèque a été ajoutée pour la première fois dans la version 1.61.0 et est toujours prise en charge.
Problèmes de connexion de boost.compute au projet
Et donc, j'ai rencontré des problèmes lors de l'utilisation de cette bibliothèque. L'un d'eux était que la bibliothèque ne fonctionne tout simplement pas sans OpenCL. Le compilateur donne l'erreur suivante:
Après la connexion, tout doit se compiler correctement.
Au détriment de la bibliothèque boost, elle peut être téléchargée et connectée à un projet Visual Studio à l'aide du gestionnaire de packages NuGet.
Introduction à boost.compute
Après avoir installé tous les composants requis, vous pouvez consulter de simples morceaux de code. Pour un fonctionnement correct, il suffit d'activer le module de calcul de cette manière:
#include <boost/compute.hpp>
using namespace boost;
Il est à noter que les conteneurs stl classiques ne sont pas adaptés à une utilisation dans les algorithmes d'espace de noms de calcul. Au lieu de cela, il existe des conteneurs spécialement créés qui n'entrent pas en conflit avec les conteneurs standard. Exemple de code:
std::vector<float> std_vector(10);
compute::vector<float> compute_vector(std_vector.begin(), std_vector.end(), queue);
// , .
Vous pouvez utiliser la fonction copy () pour reconvertir en std :: vector:
compute::copy(compute_vector.begin(), compute_vector.end(), std_vector.begin(), queue);
Classes de calcul de base
La bibliothèque comprend trois classes auxiliaires, qui suffisent pour commencer par des calculs sur une carte vidéo et / ou un processeur:
- compute :: device (déterminera avec quel appareil nous travaillerons)
- compute :: context (un objet de cette classe stocke les ressources OpenCL, y compris les tampons de mémoire et d'autres objets)
- compute :: command_queue (fournit une interface pour interagir avec un appareil informatique)
Vous pouvez déclarer tout cela comme ceci:
auto device = compute::system::default_device(); //
auto context = compute::context::context(device); //
auto queue = compute::command_queue(context, device); //
Même en utilisant simplement la première ligne de code ci-dessus, vous pouvez vous assurer que tout fonctionne comme il se doit en exécutant le code suivant:
std::cout << device.name() << std::endl;
Ainsi, nous avons obtenu le nom de l'appareil sur lequel nous allons effectuer des calculs. Résultat (vous pouvez avoir quelque chose de différent):
Commencer
Regardons les fonctions trasform () et reduction () par exemple:
std::vector<float> host_vec = {1, 4, 9};
compute::vector<float> com_vec(host_vec.begin(), host_vec.end(), queue);
//
// copy()
compute::vector<float> buff_result(host_vec.size(), context);
transform(com_vec.begin(), com_vec.end(), buff_result.begin(), compute::sqrt<float>(), queue);
std::vector<float> transform_result(host_vec.size());
compute::copy(buff_result.begin(), buff_result.end(), transform_result.begin(), queue);
cout << "Transforming result: ";
for (size_t i = 0; i < transform_result.size(); i++)
{
cout << transform_result[i] << " ";
}
cout << endl;
float reduce_result;
compute::reduce(com_vec.begin(), com_vec.end(), &reduce_result, compute::plus<float>(),queue);
cout << "Reducing result: " << reduce_result << endl;
Lorsque vous exécutez le code ci-dessus, vous devriez voir le résultat suivant:
Je me suis installé sur ces deux méthodes car elles montrent bien le travail primitif avec des calculs parallèles sans que tout soit superflu.
Et ainsi, la fonction transform () est utilisée pour changer un tableau de données (ou deux tableaux, si nous les transmettons) en appliquant une fonction à toutes les valeurs.
transform(com_vec.begin(),
com_vec.end(),
buff_result.begin(),
compute::sqrt<float>(),
queue);
Passons à l'analyse des arguments, avec les deux premiers arguments, nous passons un vecteur de données d'entrée, avec le troisième argument, nous passons un pointeur vers le début du vecteur dans lequel nous allons écrire le résultat, avec l'argument suivant, nous indiquons ce que nous devons faire. Dans l'exemple ci-dessus, nous utilisons l'une des fonctions standard de traitement vectoriel, qui consiste à extraire la racine carrée. Bien sûr, vous pouvez écrire une fonction personnalisée, boost nous fournit deux façons entières, mais c'est déjà le matériau pour la partie suivante (s'il y en a du tout). Eh bien, comme dernier argument, nous passons un objet de la classe compute :: command_queue, dont j'ai parlé ci-dessus.
La fonction suivante est réduire (), tout est un peu plus intéressant ici. Cette méthode renvoie le résultat de l'application du quatrième argument à tous les éléments du vecteur.
compute::reduce(com_vec.begin(),
com_vec.end(),
&reduce_result,
compute::plus<float>(),
queue);
Maintenant, je vais expliquer avec un exemple, le code ci-dessus peut être comparé à l'équation suivante:
Dans notre cas, nous obtenons la somme de tous les éléments du tableau.
Conclusion
Et bien c'est tout, je pense que cela suffit pour effectuer des opérations simples sur du big data. Vous pouvez maintenant utiliser la fonctionnalité primitive de la bibliothèque boost.compute, et vous pouvez également éviter certaines erreurs lorsque vous travaillez avec cette bibliothèque.
Je serais heureux de recevoir un retour positif. Merci pour votre temps.
Bonne chance à tous!