Multiplication de matrice ========================= Soit le produit de matrices :math:`C = A.B` où * :math:`A = a_{i,j} (i \in [1;H], j \in [1;Q])` * :math:`B = b_{i,j} (i \in [1;Q], j \in [1;W])` Le produit :math:`C = c_{i,j} (i \in [1;H], j \in [1;W])` a pour éléments :math:`c_{i,j} = \sum_{q \in [1;Q]} a_{i,q}b_{q,i}` Illustration .. image:: img/multiply1.png :width: 300px .. admonition:: Expérimentations :class: question * Écrire un kernel CUDA *naif* réalisant la multiplication parallèle de deux matrices. Pour ne pas compliquer trop la conception, on supposera que leurs dimensions sont des puissances de 2. On choisira comme niveau de parallèlisme : un thread calcule un coefficient du produit. * Écrire un programme *main* permettant d'exécuter la multiplication et mesurer les performances. Le *profileur* ``nvprof`` permer de diagnostiquer les points faibles du programme. La documentation de *nvprof* est accessible `ici `_. Il est possible de lister les grandeurs mesurables par .. code:: bash $ nvprof --query-metrics Parmi ces grandeurs, on peut citer - gld_efficiency - gst_efficiency - shared_efficiency - shared_replay_overhead - global_replay_overhead - global_hit_rate - local_hit_rate .. admonition:: Expérimentations :class: question * Exécuter le programme au travers du profileur pour évaluer les performances du programme. * Déduire des pistes d'amélioration possibles. Utilisation de la mémoire partagée ---------------------------------- L'idée est de décomposer la matrice résultat en blocs rectangulaires ou carrés, associés aux blocs d'exécution GPU à raison d'un coefficient par thread. Les données d'entrée seront pré-chargées en mémoire partagée. Le calcul de la somme est alors effectué avec les valeurs rangées en mémoire partagée avant d'écrire le résultat final en mémoire globale. .. admonition:: Expérimentations :class: question * Écrire un nouveau kernel de mutliplication utilisant la mémoire partagée. * Mesurer les performances.