diff --git a/Nvidia_STL.typ b/Nvidia_STL.typ index 0c99118ec69abe5cd2d2b4ee56e8f5c5dccad599..56fdcfbafece1e9776f28c3f579b30e0ad2cd5f7 100644 --- a/Nvidia_STL.typ +++ b/Nvidia_STL.typ @@ -21,8 +21,8 @@ #slide( title: "Contenu", )[ - Maintenant que nous avons les bases nous allons voir comment paralléliser notre - code avec le kit HPC de Nvidia. + Maintenant que nous avons les bases, nous allons voir comment paralléliser notre + code avec le kit HPC de Nvidia ] #new-section-slide("Le SDK HPC de Nvidia") @@ -38,7 +38,7 @@ - des librairies de communication (ex : OpenMPI) En résumé, un grand nombre d'outils pour réaliser une application HPC (basé sur - du matériel Nvidia majoritairement). + du matériel Nvidia majoritairement) #figure( image("assets/hpc-sdk_content.png"), @@ -49,9 +49,9 @@ #slide( title: "Le compilateur nvc++", )[ - Dans le cadre de ce cours, nous allons nous concentrer sur le compilateur nvc++. + Dans le cadre de ce cours, nous allons nous concentrer sur le compilateur nvc++ - Il permet du compiler du C, C++ ou CUDA. + Il permet du compiler du C, C++ ou CUDA Ce qui va nous intéresser particulièrement, c'est son support des _Parallel Algorithms_ introduits avec C++17 @@ -64,7 +64,7 @@ )[ Depuis C++17, les algorithmes de la STL peuvent prendre en paramètre une politique d'exécution. Cette politique permet d'indiquer au compilateur comment - paralléliser le traitement des données. + paralléliser le traitement des données On retrouve 4 politiques : #box( @@ -108,7 +108,7 @@ std::cout << is_sorted << std::endl; title: "Compiler avec nvc++", )[ Une fois notre code écrit, il faut le compiler avec nvc++ en indiquant comment -le paralléliser. +le paralléliser ```console nvc++ -std=c++20 -stdpar=gpu -O3 parallel_sort.cpp -o parallel_sort @@ -124,20 +124,21 @@ Nous avons trois paramètres principaux : #slide( title: "Les types de parallélisation disponibles", )[ -Le compilateur nvc++ permet de paralléliser son code sur CPU ou sur GPU. +Le compilateur nvc++ permet de paralléliser son code sur CPU ou sur GPU avec le +framework Thrust de Nvidia -Le paramètre `stdpar` prend 2 valeurs : +Le paramètre `stdpar` peut prendre 2 valeurs : #box( columns( 2, gutter: 22pt, )[ == multicore - Le code est parallélisé sur CPU avec la framework Thrust de Nvidia. + Le code est parallélisé sur un CPU multicore #colbreak() == gpu - Le code est parallélisé sur GPU avec la framework Thrust de Nvidia. Nécessite un - GPU Nvidia avec une architecture Pascal (ex: GTX 1080) ou plus récent. + Le code est parallélisé sur GPU. Nécessite un GPU Nvidia avec une architecture + Pascal (ex: GTX 1080) ou plus récent ], ) ] @@ -149,15 +150,15 @@ Le paramètre `stdpar` prend 2 valeurs : #slide( title: "Communiquer des données au GPU", )[ - Pour rappel, le GPU et CPU ont deux espaces d'adressage distincs. On ne peut + Pour rappel, le GPU et CPU ont deux espaces d'adressage distincts. On ne peut donc pas directement accéder à des données qui se trouve sur la ram de l'_Host_ - depuis le _Device_. + depuis le _Device_ - Quand on passe simplement un itérateur à un algorithme STL, il n'y a rien besoin - de faire. Le compilateur va gérer l'allocation, la copie et la libération des - données sur le device. + Quand on passe en paramètre un itérateur à un algorithme STL, il n'y a rien + besoin de faire. Le compilateur va gérer l'allocation, la copie et la libération + des données sur le device - Mais comment faire pour capturer un vecteur par exemple? + Mais comment faire pour capturer un vecteur par exemple ? ] #slide(title: "Exemple d'accès illégal")[ @@ -177,11 +178,11 @@ std::for_each(std::execution::par_unseq, ``` Ce code provoque un warning à la compilation et une erreur -`cudaErrorIllegalAddress` à l'exécution. +`cudaErrorIllegalAddress` à l'exécution ] #slide(title: "Solution")[ -Pour régler ce problème, il suffit de capturer la mémoire par copie. +Pour régler ce problème, il suffit de capturer la mémoire par copie ```cpp std::vector<int> v = {6, 3, 4, 12, 1, 15}; @@ -202,15 +203,17 @@ std::for_each(std::execution::par_unseq, #slide( title: "Le compilateur ne peut pas tout copier", )[ -Attardons nous sur cet extrait de code `[device_v = v.data(), cst]` +Attardons nous sur cet extrait de code "`[device_v = v.data(), cst]`" Le compilateur ne va pas simplement copier le pointeur `v.data()`, il va remonter à l'allocation mémoire de la zone pointée et va s'occuper de la rendre -accessible depuis le device. +accessible depuis le device Par conséquent, il n'est pas possible d'utiliser de la mémoire allouée par du code qui n'aurait pas été compilé par nvc++ (ex: une librairie paratagée comme -OpenCV). +OpenCV) + +#v(20%) Le compilateur s'appuie principalement sur la CUDA Unified memory. Pour plus d'information à ce sujet, je vous recommande ce billet de blog : @@ -220,18 +223,18 @@ d'information à ce sujet, je vous recommande ce billet de blog : #new-section-slide("Le futur avec C++23 (views et mdspans)") #slide( - title: "", + title: "Le futur avec C++23", )[ La prochaine étape majeure dans ce paradigme apparaît avec C++23. Depuis C++ 20, - les spans et les views ont fait leur apparition. + les spans et les views ont fait leur apparition - Les views sont une forme d'itérateurs à évaluation paresseuse. + Les views sont une forme d'itérateurs à évaluation paresseuse Dans C++23, on voit apparaître de nouvelles méthodes pour combiner les views - comme par exemple le produit cartésien pour générer les indices d'une matrice. + comme par exemple le produit cartésien pour générer les indices d'une matrice Et la grande nouveauté, les mdspans qui permettent de gérer des structures de - données multidimensionnelles. + données multidimensionnelles ] #new-section-slide("Exemple de code C++23 et notions à retenir")