Donnez votre avis !
                   Depuis votre smartphone, sur :
                    http://notes.mstechdays.fr

    De nombreux lots à gagner toutes les heures !!!
               Claviers, souris et jeux Microsoft…

       Merci de nous aider à améliorer les TechDays

http://notes.mstechdays.fr
La programmation GPU
        avec C++ AMP pour les
        performances extrêmes
                               Bruno Boucard
                              Expert Parallèle
                                    FINAXYS


  LAN401               http://blogs.msdn.com/devpara/

Code / Développement
CONSEIL ET ARCHITECTURE IT-
         •

         •
             160 collaborateurs spécialisés en IT-Finance

             CA 2012 (prév.) de 16M€

         FINANCE
         •   Une large palette d’expertises :

                    •   Conseil SI & fonctionnel Finance,

                    •   AMOA SI et métiers Finance

                    •   Ingénierie logicielle (architecture, études, conception, développement, intégration continue)

                          •     Technologies Microsoft C++ et C#/.NET

                    •   Support et production applicative

         •   Un accompagnement global sur l’ensemble du cycle de vie projet, du cadrage à la recette, avec une démarche qualité
         intégrée

         •   Une contractualisation sur mesure : forfait, centre de service, assistance technique groupée ou assistance technique
         simple

         •   Nos références :




Ma société
Démo Cartoonizer !



De quoi allons-nous parler ?
Agenda
     • 1ière Partie : L’essentiel de C++ AMP
      – Contexte
      – Fondamentaux
     • 2ième Partie: Concepts plus avancés
      – Comprendre le calcul tuilé
      – Mise au point avec Visual Studio 2012
     • Conclusion

La programmation GPU avec C++ AMP
CPU vs GPU aujourd’hui


       •   Faible bande passante mémoire           •   Bande passante mémoire élevée
       •   Forte consommation d’énergie/FLOP       •   Faible consommation d'énergie/FLOP
       •   Niveau moyen de parallélisme            •   Haut niveau de parallélisme
       •   Pipelines d’exécution profonds          •   Pipelines d'exécution peu profonds
       •   Accès cache mémoire aléatoires          •   Accès cache mémoire séquentiels
       •   Supporte la programmation généraliste   •   Supporte la programmation parallèle orientée
       •   Programmation grand public                  données

                                                   •   Programmation réservée à des spécialistes
Contexte
CPU vs GPU aujourd’hui

                                                             image
     • CPUs et GPUs se rapprochent de plus en plus …      source: AMD

     • …mais rien n’est figé, le sujet est toujours en mouvement …

     • C++ AMP a été pensé comme une solution grand public pas
       seulement pour aujourd’hui, mais aussi pour demain




Contexte
Plateformes d’exécution de C++ AMP*
      •   Windows Azure                         •   Windows Phone
                                                •   Windows RT
      •   Windows Desktop
                                                •   Windows Embedded
      •   Windows Server
                                                •   Xbox
      •   Windows HPC Server




        ... tourne aussi sur des plateformes non Microsoft via une spécification
        ouverte
Contexte        * Certaines plateformes seront disponibles dans la prochaine version de C++ AMP
Qu’est que C++ AMP ?
    • Appartient à Visual C++
    • Intégrée à Visual Studio
    • Librairie STL-like pour gérer
      les données                     performance
      multidimensionnelles
    • Construite sur Direct3D 11      productivité
    • Spécification ouverte
                                      portabilité
Contexte
Transformer ce code séquentiel en code
          GPU                                           #include <amp.h>
                                                        using namespace concurrency;

  void AddArrays(int n, int * pA, int * pB, int * pC)   void AddArrays(int n, int * pA, int * pB, int * pC)
  {                                                     {
                                                          array_view<int,1> a(n, pA);
                                                          array_view<int,1> b(n, pB);
                                                          array_view<int,1> c(n, pC);

      for (int i=0; i<n; i++)                               parallel_for_each(
                                                            for (int i=0; i<n; i++)
                                                                c.extent,
                                                                [=](index<1> i) restrict(amp)
      {                                                         {
           pC[i] = pA[i] + pB[i];                                  c[i] = = pA[i] + pB[i];
                                                                   pC[i] a[i] + b[i];
      }                                                         }
                                                             );
  }                                                     }




Fondamentaux
Comprendre le code C++ AMP
    parallel_for_each:          void AddArrays(int n, int * pA, int * pB, int * pC)
                                                                           restrict(amp): dit au compilateur de
    exécute l’expression        {                                          forcer l’exécution de ce code sur le
    lambda sur                    array_view<int,1> a(n, pA);              GPU
    l’accélérateur une fois       array_view<int,1> b(n, pB);
    par thread
                                  array_view<int,1> c(n, pC);
                                                           array_view: wrapping des
                                  parallel_for_each(       données pour les traiter sur
                                                           l’accélérateur
    extent: le nombre de
                                           c.extent,
    threads et leur topologie              [=](index<1> i) restrict(amp)
    pour exécuter                          {
    l’expression lambda                         c[i] = a[i] + b[i];
                                           }
                                  );                                Les variables array_view sont
                                                                  capturées par valeur et leurs
    index: le thread ID qui exécute l’expression
                                 }                                données associées sont copiées
    lambda, est utilisé pour retrouver les données
                                                                  dans l’accélérateur à la demande

Fondamentaux
Comprendre le fonctionnement du
     parallel_for_each
     •   Exécute l’expression lambda pour chaque point de l’extent
     •   Semble synchrone, mais en réalité est asynchrone

                                                                                                                                                                                 parallel_for_each(
                                                                                                                                                                                      c.extent,
                                                                                                                                                                                      [=](index<1> idx) restrict(amp)
                              c[1] = a[1] + b[1]
         c[0] = a[0] + b[0]


                                                   c[2] = a[2] + b[2]
                                                                        c[3] = a[3] + b[3]
                                                                                             c[4] = a[4] + b[4]
                                                                                                                  c[5] = a[5] + b[5]
                                                                                                                                       c[6] = a[6] + b[6]
                                                                                                                                                            c[7] = a[7] + b[7]
                                                                                                                                                                                      {




                                                                                                                                                                                                                   Code noyau
                                                                                                                                                                                        c[idx] = a[idx] + b[idx];
                                                                                                                                                                                      }
                                                                                                                                                                                   );
                                                                                                                                                                                 }
          0                    1                     2                    3                    4                    5                    6                    7


Fondamentaux
Le mot clef restrict

    • S'applique aux fonctions (y compris les lambda)
    • resctrict(…) informe le compilateur d’appliquer des
      restrictions au langage
       – e.g. restrictions spécifiques, optimisations,
         génération de code
       – Dans cette 1ère version, supporte deux directives
          • cpu – est le défaut implicite
          • amp – vérifie la conformité de la fonction vis à vis
             de C++ AMP
Fondamentaux
Les restrictions du mot clef restrict
     • Pas de                                   • Pas de
       • récusions                               • goto ou labels
       • ‘volatile'                              • throw, try, catch
       • fonctions virtuelles                    • Variables globales ou statiques
       • pointeurs de fonctions                  • dynamic_cast ou typeid
       • pointeurs vers des fonctions membres    • déclarations __asm__
       • pointeurs dans des structures           • varargs
       • pointeurs de pointeurs                  • types non supportés
       • champs de bits                          •   e.g. char, short, long double
Fondamentaux
Surcharge via le mot clef restrict
       double cos( double d );                            // 1a code cpu
       double cos( double d ) restrict(amp);              // 1b code amp
       double bar( double d ) restrict(cpu,amp);          // 2 supporte les deux mode

       void some_method(array_view<double,2> c) {
         parallel_for_each( c.extent, [=](index<2> idx) restrict(amp)
         {
           //…
           double d0 = c[idx];
           double d1 = bar(d0); // ok, bar supporte amp aussi
           double d2 = cos(d0); // ok, sélection de la surcharge amp
           //…
         });
       }




Fondamentaux
Gestion des données multidimensionnelles
     •   index<N> - représente un point à N coordonnées
     •   extent<N> - taille de la données d’un espace à N dimensions
                                       index<2> i(0,2);           index<3> i(2,0,1);
          index<1> i(2);

                                   3                               2
               6                                                                 3
         extent<1> e(6);                    4                            2
                                extent<2> e(3,4);              extent<3> e(3,2,2);
     •   N peut être n’importe quel nombre <=128




Fondamentaux
Le conteneur: array_view<T, N>
      •       Vue sur les données stockées dans la mémoire CPU ou
              la mémoire GPU
      •       Capture par valeur [=] dans l’expression lambda
      •       De type T et de rang N
      •       Réclame un extent
                                                     vector<int> v(10);
      •       Rectangulaire                         extent<2> e(2,5); array_view<int,2> a(e, v);

      •       Accès aux éléments déclenchent
      •       des copies implicites
          index<2> i(3,9);                                  // Les 2 lignes peuvent aussi s’écrire
                                                            // array_view<int,2> a(2,5,v);
          int o = a[i]; // ou a[i] = 16;
          // ou int o = a(3, 9);


Fondamentaux
Le second conteneur: array<T, N>
        •   Tableau multidimensionnel qui contient des éléments de type T de dimension N

        •   Les données sont stockées dans la mémoire de l'accélérateur

        •   Capture par référence [&] dans l’expression lambda

        •   Copie explicit

        •   Presque identique à l’interface array_view<T,N>

    vector<int> v(8 * 12);                       parallel_for_each(e, [&](index<2> idx) restrict(amp)
    extent<2> e(8,12);                           {
                                                     a[idx] += 1;
    array<int,2> a(e);                           });
    copy_async(v.begin(), v.end(), a);           copy(a, v.begin());

Fondamentaux
Comprendre les accélérateurs                                   Host                Accélérateurs
     • accelerator
         – DX11 GPU
             • Support GPU
                                                                                PCIe
         – WARP (Windows Advanced Rasterisation
           Platform)
             • multi-core & vectorisazion (SSE)
             • Accessible via Direct3D
         – REF
             • Emulateur qui simule un pilote direct3d sur •   accelerator View
                 CPU                                            – Contexte pour
         – CPU                                                    ordonnancer les threads
                                                                  GPU et gérer les données
             • Support pour optimiser les transferts de
                                                                  en mémoire
                 données entre l'hôte et un
                 accelerator_view
Fondamentaux
Démo Accélérateurs!




Fondamentaux
Algorithme produit matriciel
  • Aij * Bjk = Cik
      – Chaque ligne A est multipliée par toutes les
        colonnes entières de B
      – Chaque multiplication est sommée dans une
        variable temporaire
      – Après avoir multiplié tous les éléments de la
        ligne avec tous les éléments de la colonne
        courante
      – Le résultat temporaire est placé dans la cellule
        correspondante de la « matrice produit » C

Fondamentaux
Produit matriciel du CPU au GPU
void MatrixMultiplySerial( vector<float>& vC,         void MatrixMultiplyAMP( vector<float>& vC,
  const vector<float>& vA,                              const vector<float>& vA,
  const vector<float>& vB, int M, int N, int W )        const vector<float>& vB, int M, int N, int W )
{                                                     {
                                                        array_view<const float,2> a(M,W,vA),b(W,N,vB);
                                                        array_view<float,2> c(M,N,vC);c.discard_data();
                                                        parallel_for_each(c.extent,
    for (int row = 0; row < M; row++) {                    [=](index<2> idx) restrict(amp) {
      for (int col = 0; col < N; col++){                       int row = idx[0]; int col = idx[1];
        float sum = 0.0f;                                         float sum = 0.0f;
        for(int i = 0; i < W; i++)                                for(int i = 0; i < W; i++)
          sum += vA[row * W + i] * vB[i * N + col];                 sum += a(row, i) * b(i, col);
        vC[row * N + col] = sum;                                  c[idx] = sum;
      }                                                    }
    }                                                   );
}                                                     }


Fondamentaux
Démo Produit Matriciel !



Fondamentaux
1ière Partie: ce que vous avez appris !
    Sur des volumes de données immenses, le calcul sur GPU offre des performances
     extraordinaires
    Microsoft souhaite démocratiser la programmation GPU avec C++ AMP sur toutes les
     plateformes

   Les éléments fondamentaux
         class index<N> : pointe une valeur dans un conteneur multidimensionnel
         class extent<N> : taille d’un conteneur multidimensionnel
         restrict(amp, cpu) : restreint la syntaxe C++ pour exécuter une méthode ou une
          lambda en mode noyau
         parallel_for_each : exécute une boucle en parallèle sur des données
          multidimensionnelle
         class array_view<T,N> : wrapper multidimensionnel sur des données
         class accelerator : accélérateur utilisé en interne pour exécuter le code parallèle
         class accelerator_view : isole l’accélérateur des différents usages multithreads
         class array<T,N> : conteneur multidimensionnel sur des données
Résumé de la première partie
Une vue du matériel GPU pour un développeur
                                                        Thread

                       …                           …
                     … ……                        … ……
                             Registres par Threads

                                                        N’est pas montré:
                                                        • Mémoire constante
                                                        • Mémoire des
                               Mémoire Globale             contrôleurs
                                                        • Schedulers
                                                        • Autres caches
                                                        • Cas Multi-GPU


Comprendre le calcul tuilé
Une vue du matériel GPU pour un développeur

                     …                                 …           Tuile composée de
                                                                   threads


                   … ……                              … ……
           Registres par Threads           Registres par Threads

                                                                       N’est pas montré:
                                                                       • Mémoire constante
                                                                       • Mémoire des
                                   Mémoire Globale                        contrôleurs
                                                                       • Schedulers
                                                                       • Autres caches
                                                                       • Cas Multi-GPU


Comprendre le calcul tuilé
Une vue du matériel GPU pour un développeur

                     …                                 …           Tuile composée de
                                                                   threads


                   … ……                              … ……          Les variable tile_static
                                                                   sont partagée par les
                                                                   threads dans la même
                                                                   tuile
           Registres par Threads            Registres par Thread
          Cache Programmable               Cache Programmable          N’est pas montré:
                                                                       • Mémoire constante
                                                                       • Mémoire des contrôleurs
                                                                       • Schedulers
                                   Mémoire Globale                     • Autres caches
                                                                       • Cas Multi-GPU




Comprendre le calcul tuilé
parallel_for_each: surcharge pour les tuiles
        • Orchestrer des threads           array_view<int,1> data(12, my_data);
          dans des tuiles
           – La version de base ne
               le permet pas               parallel_for_each(data.extent,
        • parallel_for_each version          [=] (index<1> idx) restrict(amp)
          tuile accepte ces                  { … });
          surcharges
              – tiled_extent<D0> ou
                tiled_extent<D0, D1> ou    parallel_for_each(data.extent.tile<6>(),
                tiled_extent<D0, D1, D2>
        • L’expression lambda                [=] (tiled_index<6> t_idx) restrict(amp)
          accepte                            { … });
              – tiled_index<D0> ou
                tiled_index<D0, D1> ou
                tiled_index<D0, D1, D2>

Comprendre le calcul tuilé
tiled_extent (depuis un extent)
                    extent<1> e(12);
                     0     1     2        3   4      5         6    7     8      9    10     11

                          tiled_extent<6> t_e = e.tile<6>();
                           0     1        2   3      4         5    6     7      8     9     10     11




                    extent<2> ee(2, 6);                            tiled_extent<2, 2> t_ee = ee.tile<2, 2>();
                    0,0   0,1   0,2    0,3    0,4   0,5                  0,0    0,1   0,2   0,3    0,4    0,5

                    1,0   1,1   1,2    1,3    1,4   1,5                  1,0    1,1   1,2   1,3    1,4    1,5




Techniques plus avancées: calcul tuilé
tiled_index
      • Soit                                           col 0   col 1       col 2   col 3   col 4   col 5

        array_view<int,2> data(2, 6, p_my_data); row
        parallel_for_each(                        0
          data.extent.tile<2,2>(),
          [=] (tiled_index<2,2> t_idx)… { … });
                                                 row
                                                  1
                                                                                    T

      • Lorsque la lambda est exécutée par                             T

            –   t_idx.global        // index<2> (1,3)
            –   t_idx.local         // index<2> (1,1)
            –   t_idx.tile          // index<2> (0,1)
            –   t_idx.tile_origin   // index<2> (0,2)

Comprendre le calcul tuilé
tile_static
        • Le mot clef tile_static dédié au stockage mémoire
        • Second mot clef ajouté au langage C++
        • Représente la mémoire locale du GPU

        • Usage du mot clé tile_static
           – Dans une boucle parallel_for_each tuilée
           – Le mot clef tile_static devant une déclaration de
             variable locale place cette variable dans la mémoire
             locale GPU pour la tuile courante
           – Cette mémoire est partagée par tous les threads
             associés à la tuile
           – Applicable seulement pour les fonctions restrict(amp)


Comprendre le calcul tuilé
tile_barrier
      • classe tile_barrier
            – Synchronise tous les threads au sein d’une tuile jusqu'à
              ce que tous les accès à la mémoire soient terminés
                 • t_idx.barrier.wait();
                 • ~ t_idx.barrier.wait_with_all_memory_fence();

            – Synchronise tous les threads au sein d’une tuile jusqu'à
              ce que tous les accès à la mémoire globale soient
              terminés
                 • t_idx.barrier.wait_with_global_memory_fence();

            – Synchronise tous les threads au sein d’une tuile jusqu'à
              ce que tous les accès à la mémoire la mémoire tile_static
              (locale) soient terminés
                 • t_idx.barrier.wait_with_tile_static_memory_fence();


Comprendre le calcul tuilé
Cacher avec tile_static                0,0 0,1 0,2 0,3 0,4 0,5
                                              1,0 1,1 1,2 1,3 1,4 1,5
   1    static const int TS = 2;
   2    array_view<int, 2> av(2, 6, my_vector);
   3    parallel_for_each(av.extent.tile<TS,TS>(),
        [=](tiled_index<TS,TS> t_idx) restrict(amp)
   4    {
   5        tile_static int t[TS][TS];
   6        t[t_idx.local[0]][t_idx.local[1]] = av[t_idx.global];
   7
   8      if (t_idx.local == index<2>(0,0)) {
   9         int temp = t[0][0] + t[0][1] + t[1][0] + t[1][1];
   10        av[t_idx.tile_origin] = temp;
   11     }
   12 });
   13 int sum = av(0,0) + av(0,2) + av(0,4); //the three tile_origins


Comprendre le calcul tuilé
tile_static en action                  0,0 0,1 0,2 0,3 0,4 0,5
                                              1,0 1,1 1,2 1,3 1,4 1,5
   1    static const int TS = 2;
   2    array_view<int, 2> av(2, 6, my_vector);
   3    parallel_for_each(av.extent.tile<TS,TS>(),
        [=](tiled_index<TS,TS> t_idx) restrict(amp)
   4    {
   5        tile_static int t[TS][TS];
   6        t[t_idx.local[0]][t_idx.local[1]] = av[t_idx.global];
   7
   8      if (t_idx.local == index<2>(0,0)) {
   9         int temp = t[0][0] + t[0][1] + t[1][0] + t[1][1];
   10        av[t_idx.tile_origin] = temp;
   11     }
   12 });
   13 int sum = av(0,0) + av(0,2) + av(0,4); //the three tile_origins


Comprendre le calcul tuilé
Synchro avec tile_barrier0,0               0,1 0,2 0,3 0,4 0,5
                                              1,0 1,1 1,2 1,3 1,4 1,5
   1    static const int TS = 2;
   2    array_view<int, 2> av(2, 6, my_vector);
   3    parallel_for_each(av.extent.tile<TS,TS>(),
        [=](tiled_index<TS,TS> t_idx) restrict(amp)
   4    {
   5        tile_static int t[TS][TS];
   6        t[t_idx.local[0]][t_idx.local[1]] = av[t_idx.global];
   7        tile_barrier.wait();
   8        if (t_idx.local == index<2>(0,0)) {
   9           int temp = t[0][0] + t[0][1] + t[1][0] + t[1][1];
   10          av[t_idx.tile_origin] = temp;
   11       }
   12   });
   13   int sum = av(0,0) + av(0,2) + av(0,4); //the three tile_origins


Comprendre le calcul tuilé
Retour sur la Multiplication Matricielle
       • Chargement des données pour 4 threads




       • Les threads chargent plusieurs fois la même
         valeur depuis la mémoire globale !
             – Comment éviter ce problème ?

Comprendre le calcul tuilé
Multiplication Matricielle – Exemple (tuilée)
 void MatrixMultSimple(vector<float>& vC, const vector<float>& vA, const   void MatrixMultTiled(vector<float>& vC, const vector<float>& vA, const
                                                                           void
 vector<float>& vB, int M, int N, int W )                                  vector<float>& vB, int M, int N,           W)
                                                                           const vector<float>& vB, int M, int N, int W )
 {                                                                         {
                                                                             static const int TS = 16;
                                                                                     const int TS = 16;
     array_view<const float,2> a(M, W, vA), b(W, N, vB);                     array_view<const float,2> a(M, W, vA), b(W, N, vB);
     array_view<float,2> c(M,N,vC); c.discard_data();                        array_view<float,2> c(M,N,vC); c.discard_data();
     parallel_for_each(c.extent,                                             parallel_for_each(c.extent.tile< TS, TS >(),
                                                                                                                     TS, TS >(),
       [=] (index<2> idx) restrict(amp) {                                       [=] (tiled_index<TS, TS> t_idx) restrict(amp) { {
                                                                                    (tiled_index< TS, TS> t_idx) restrict(amp)
         int row = idx[0]; int col = idx[1];                                      int row = t_idx.local[0]; int col = t_idx.local[1];
                                                                                                                          t_idx.local[1];
         float sum = 0.0f;                                                        float sum = 0.0f;
                                                                                  for (int i = 0; ii < W; ii += TS) {{
                                                                                               0; < W; += TS)
                                                                                      tile_static float locA[TS][TS], locB[TS][TS];
                                                                                      tile_static float locA[TS][TS], locB[TS][TS];




                                                                                                                                         Phase 1
                                                                                      locA[row][col] = a(t_idx.global[0], col i);
                                                                                      locA[row][col]= a(t_idx.global[0], col ++ i);
                                                                                      locB[row][col] = b(row + i, t_idx.global[1]);
                                                                                      locB[row][col] = b(row + i, t_idx.global[1]);
                                                                                      t_idx.barrier.wait();
                                                                                      t_idx.barrier.wait();

         for(int k = 0; k < W; k++)                                                for (int k = 0; k < TS; k++)




                                                                                                                                         Phase 2
           sum += a(row, k) * b(k, col);                                             sum += locA[row][k] * locB[k][col];
                                                                                   t_idx.barrier.wait();
                                                                                   t_idx.barrier.wait();
                                                                                    }
          c[idx] = sum;                                                           c[t_idx.global] = sum;
                                                                                  c[t_idx.global] = sum;
       } );                                                                    } );
 }                                                                         }

Techniques plus avancées: calcul tuilé
Démo Produit Matriciel Tuilé !




Comprendre le calcul tuilé
C++ AMP Parallel Debugger
      • Fonctionnalités bien connues du débogage Visual studio
            – Launch, Attach, Break, Stepping, Breakpoints, DataTips
            – Outillage orienté fenêtre
               • Processes, Debug Output, Modules, Disassembly, Call Stack,
                  Memory, Registers, Locals, Watch, Quick Watch
      • Nouvelles fonctionnalités (à la fois le CPU et le GPU)
            – Parallel Stacks window, Parallel Watch window, Barrier
      • Nouveautés spécifiques GPU
            – Emulateur, fenêtre GPU Threads , détection de races conditions

Visual Studio
Debugger GPU en Action




Visual Studio
2ième Partie: ce que vous avez appris !
  Comprendre le calcul tuilé
        La macro architecture GPU pour les développeurs
        La classe tile_static pour cacher des données pour un groupe de threads
        Le classe tiled_extent< , , > permet à la surcharge parallel_foreach de simplifier le
         calcul en tuilé
        La classe tiled_index< , , > permet à la surcharge parallel_foreach de simplifier le
         calcul en tuilé
        La classe tile_barrier permet de synchroniser les threads au sein d’une tuile
  Mise au point avec Visual Studio
        Configurer le mode Debug GPU
        Détecter les races conditions
        Utiliser la fenêtre Parallel GPU pour observer l’état de tous les threads et naviguer
         entre eux
        Utiliser la fenêtre Parallel Watch pour observer des données pour chacun des threads
        Exécuter toute une tuile de threads jusqu’à votre curseur
Résumé de la seconde partie
Conclusion
     • Démocratisation de la programmation parallèle hétérogène
        – Haute performance accessible au grand public
        – Abstraction de la plateforme matérielle
        – Abstractions de haut niveau en C++ (pas en langage C)
        – Repose sur un jeu minimal d’API
        – C++ AMP est une spécification libre
        – Etat de l’art de l’IDE de Visual Studio


C++ AMP en quelques mots
Pour aller plus loin …
      MSDN Forums to ask questions
      •    http://social.msdn.microsoft.com/Forums/en/parallelcppnative/threads

      MSDN Native parallelism blog (team blog)
      •    http://blogs.msdn.com/nativeconcurrency/
                                                                                          Libraries for C++ AMP
      C++ AMP Samples                                                                     •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/05/19/libraries-for-
      •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/01/30/c-amp-sample-          c-amp.aspx
           projects-for-download.aspx

      Graphics in C++ AMP                                                                 C++ AMP for the OpenCLProgrammer
      •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/01/25/concurrency-
                                                                                          •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/04/10/c-amp-for-
           graphics-in-c-amp.aspx                                                              the-opencl-programmer.aspx

      Interoperability between Direct 3D and C++ AMP                                      C++ AMP for the CUDA Programmer
      •    http://blogs.msdn.com/b/nativeconcurrency/archive/2011/12/29/interoperabilit   •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/04/11/c-amp-for-
           y-between-direct-3d-and-c-amp.aspx                                                  the-cuda-programmer.aspx

      C++ AMP open spec                                                                   C++ AMP for the DirectCompute Programmer
      •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/02/03/c-amp-open-       •    http://blogs.msdn.com/b/nativeconcurrency/archive/2012/04/09/c-amp-for-
           spec-published.aspx                                                                 the-directcompute-programmer.aspx




Pour creuser un peu plus …
Développeurs                                                         Pros de l’IT
 http://aka.ms/generation-app       Formez-vous en ligne        www.microsoftvirtualacademy.com

    http://aka.ms/evenements-
                 developpeurs     Retrouvez nos évènements      http://aka.ms/itcamps-france


            Les accélérateurs
                                  Faites-vous accompagner
Windows Azure, Windows Phone,
                                  gratuitement
                   Windows 8


                                   Essayer gratuitement nos     http://aka.ms/telechargements
                                                 solutions IT

         La Dev’Team sur MSDN       Retrouver nos experts       L’IT Team sur TechNet
          http://aka.ms/devteam           Microsoft             http://aka.ms/itteam

La programmation GPU avec C++ AMP pour les performances extrêmes

  • 1.
    Donnez votre avis! Depuis votre smartphone, sur : http://notes.mstechdays.fr De nombreux lots à gagner toutes les heures !!! Claviers, souris et jeux Microsoft… Merci de nous aider à améliorer les TechDays http://notes.mstechdays.fr
  • 2.
    La programmation GPU avec C++ AMP pour les performances extrêmes Bruno Boucard Expert Parallèle FINAXYS LAN401 http://blogs.msdn.com/devpara/ Code / Développement
  • 3.
    CONSEIL ET ARCHITECTUREIT- • • 160 collaborateurs spécialisés en IT-Finance CA 2012 (prév.) de 16M€ FINANCE • Une large palette d’expertises : • Conseil SI & fonctionnel Finance, • AMOA SI et métiers Finance • Ingénierie logicielle (architecture, études, conception, développement, intégration continue) • Technologies Microsoft C++ et C#/.NET • Support et production applicative • Un accompagnement global sur l’ensemble du cycle de vie projet, du cadrage à la recette, avec une démarche qualité intégrée • Une contractualisation sur mesure : forfait, centre de service, assistance technique groupée ou assistance technique simple • Nos références : Ma société
  • 4.
    Démo Cartoonizer ! Dequoi allons-nous parler ?
  • 5.
    Agenda • 1ière Partie : L’essentiel de C++ AMP – Contexte – Fondamentaux • 2ième Partie: Concepts plus avancés – Comprendre le calcul tuilé – Mise au point avec Visual Studio 2012 • Conclusion La programmation GPU avec C++ AMP
  • 6.
    CPU vs GPUaujourd’hui • Faible bande passante mémoire • Bande passante mémoire élevée • Forte consommation d’énergie/FLOP • Faible consommation d'énergie/FLOP • Niveau moyen de parallélisme • Haut niveau de parallélisme • Pipelines d’exécution profonds • Pipelines d'exécution peu profonds • Accès cache mémoire aléatoires • Accès cache mémoire séquentiels • Supporte la programmation généraliste • Supporte la programmation parallèle orientée • Programmation grand public données • Programmation réservée à des spécialistes Contexte
  • 7.
    CPU vs GPUaujourd’hui image • CPUs et GPUs se rapprochent de plus en plus … source: AMD • …mais rien n’est figé, le sujet est toujours en mouvement … • C++ AMP a été pensé comme une solution grand public pas seulement pour aujourd’hui, mais aussi pour demain Contexte
  • 8.
    Plateformes d’exécution deC++ AMP* • Windows Azure • Windows Phone • Windows RT • Windows Desktop • Windows Embedded • Windows Server • Xbox • Windows HPC Server ... tourne aussi sur des plateformes non Microsoft via une spécification ouverte Contexte * Certaines plateformes seront disponibles dans la prochaine version de C++ AMP
  • 9.
    Qu’est que C++AMP ? • Appartient à Visual C++ • Intégrée à Visual Studio • Librairie STL-like pour gérer les données performance multidimensionnelles • Construite sur Direct3D 11 productivité • Spécification ouverte portabilité Contexte
  • 10.
    Transformer ce codeséquentiel en code GPU #include <amp.h> using namespace concurrency; void AddArrays(int n, int * pA, int * pB, int * pC) void AddArrays(int n, int * pA, int * pB, int * pC) { { array_view<int,1> a(n, pA); array_view<int,1> b(n, pB); array_view<int,1> c(n, pC); for (int i=0; i<n; i++) parallel_for_each( for (int i=0; i<n; i++) c.extent, [=](index<1> i) restrict(amp) { { pC[i] = pA[i] + pB[i]; c[i] = = pA[i] + pB[i]; pC[i] a[i] + b[i]; } } ); } } Fondamentaux
  • 11.
    Comprendre le codeC++ AMP parallel_for_each: void AddArrays(int n, int * pA, int * pB, int * pC) restrict(amp): dit au compilateur de exécute l’expression { forcer l’exécution de ce code sur le lambda sur array_view<int,1> a(n, pA); GPU l’accélérateur une fois array_view<int,1> b(n, pB); par thread array_view<int,1> c(n, pC); array_view: wrapping des parallel_for_each( données pour les traiter sur l’accélérateur extent: le nombre de c.extent, threads et leur topologie [=](index<1> i) restrict(amp) pour exécuter { l’expression lambda c[i] = a[i] + b[i]; } ); Les variables array_view sont capturées par valeur et leurs index: le thread ID qui exécute l’expression } données associées sont copiées lambda, est utilisé pour retrouver les données dans l’accélérateur à la demande Fondamentaux
  • 12.
    Comprendre le fonctionnementdu parallel_for_each • Exécute l’expression lambda pour chaque point de l’extent • Semble synchrone, mais en réalité est asynchrone parallel_for_each( c.extent, [=](index<1> idx) restrict(amp) c[1] = a[1] + b[1] c[0] = a[0] + b[0] c[2] = a[2] + b[2] c[3] = a[3] + b[3] c[4] = a[4] + b[4] c[5] = a[5] + b[5] c[6] = a[6] + b[6] c[7] = a[7] + b[7] { Code noyau c[idx] = a[idx] + b[idx]; } ); } 0 1 2 3 4 5 6 7 Fondamentaux
  • 13.
    Le mot clefrestrict • S'applique aux fonctions (y compris les lambda) • resctrict(…) informe le compilateur d’appliquer des restrictions au langage – e.g. restrictions spécifiques, optimisations, génération de code – Dans cette 1ère version, supporte deux directives • cpu – est le défaut implicite • amp – vérifie la conformité de la fonction vis à vis de C++ AMP Fondamentaux
  • 14.
    Les restrictions dumot clef restrict • Pas de • Pas de • récusions • goto ou labels • ‘volatile' • throw, try, catch • fonctions virtuelles • Variables globales ou statiques • pointeurs de fonctions • dynamic_cast ou typeid • pointeurs vers des fonctions membres • déclarations __asm__ • pointeurs dans des structures • varargs • pointeurs de pointeurs • types non supportés • champs de bits • e.g. char, short, long double Fondamentaux
  • 15.
    Surcharge via lemot clef restrict double cos( double d ); // 1a code cpu double cos( double d ) restrict(amp); // 1b code amp double bar( double d ) restrict(cpu,amp); // 2 supporte les deux mode void some_method(array_view<double,2> c) { parallel_for_each( c.extent, [=](index<2> idx) restrict(amp) { //… double d0 = c[idx]; double d1 = bar(d0); // ok, bar supporte amp aussi double d2 = cos(d0); // ok, sélection de la surcharge amp //… }); } Fondamentaux
  • 16.
    Gestion des donnéesmultidimensionnelles • index<N> - représente un point à N coordonnées • extent<N> - taille de la données d’un espace à N dimensions index<2> i(0,2); index<3> i(2,0,1); index<1> i(2); 3 2 6 3 extent<1> e(6); 4 2 extent<2> e(3,4); extent<3> e(3,2,2); • N peut être n’importe quel nombre <=128 Fondamentaux
  • 17.
    Le conteneur: array_view<T,N> • Vue sur les données stockées dans la mémoire CPU ou la mémoire GPU • Capture par valeur [=] dans l’expression lambda • De type T et de rang N • Réclame un extent vector<int> v(10); • Rectangulaire extent<2> e(2,5); array_view<int,2> a(e, v); • Accès aux éléments déclenchent • des copies implicites index<2> i(3,9); // Les 2 lignes peuvent aussi s’écrire // array_view<int,2> a(2,5,v); int o = a[i]; // ou a[i] = 16; // ou int o = a(3, 9); Fondamentaux
  • 18.
    Le second conteneur:array<T, N> • Tableau multidimensionnel qui contient des éléments de type T de dimension N • Les données sont stockées dans la mémoire de l'accélérateur • Capture par référence [&] dans l’expression lambda • Copie explicit • Presque identique à l’interface array_view<T,N> vector<int> v(8 * 12); parallel_for_each(e, [&](index<2> idx) restrict(amp) extent<2> e(8,12); { a[idx] += 1; array<int,2> a(e); }); copy_async(v.begin(), v.end(), a); copy(a, v.begin()); Fondamentaux
  • 19.
    Comprendre les accélérateurs Host Accélérateurs • accelerator – DX11 GPU • Support GPU PCIe – WARP (Windows Advanced Rasterisation Platform) • multi-core & vectorisazion (SSE) • Accessible via Direct3D – REF • Emulateur qui simule un pilote direct3d sur • accelerator View CPU – Contexte pour – CPU ordonnancer les threads GPU et gérer les données • Support pour optimiser les transferts de en mémoire données entre l'hôte et un accelerator_view Fondamentaux
  • 20.
  • 21.
    Algorithme produit matriciel • Aij * Bjk = Cik – Chaque ligne A est multipliée par toutes les colonnes entières de B – Chaque multiplication est sommée dans une variable temporaire – Après avoir multiplié tous les éléments de la ligne avec tous les éléments de la colonne courante – Le résultat temporaire est placé dans la cellule correspondante de la « matrice produit » C Fondamentaux
  • 22.
    Produit matriciel duCPU au GPU void MatrixMultiplySerial( vector<float>& vC, void MatrixMultiplyAMP( vector<float>& vC, const vector<float>& vA, const vector<float>& vA, const vector<float>& vB, int M, int N, int W ) const vector<float>& vB, int M, int N, int W ) { { array_view<const float,2> a(M,W,vA),b(W,N,vB); array_view<float,2> c(M,N,vC);c.discard_data(); parallel_for_each(c.extent, for (int row = 0; row < M; row++) { [=](index<2> idx) restrict(amp) { for (int col = 0; col < N; col++){ int row = idx[0]; int col = idx[1]; float sum = 0.0f; float sum = 0.0f; for(int i = 0; i < W; i++) for(int i = 0; i < W; i++) sum += vA[row * W + i] * vB[i * N + col]; sum += a(row, i) * b(i, col); vC[row * N + col] = sum; c[idx] = sum; } } } ); } } Fondamentaux
  • 23.
  • 24.
    1ière Partie: ceque vous avez appris !  Sur des volumes de données immenses, le calcul sur GPU offre des performances extraordinaires  Microsoft souhaite démocratiser la programmation GPU avec C++ AMP sur toutes les plateformes Les éléments fondamentaux  class index<N> : pointe une valeur dans un conteneur multidimensionnel  class extent<N> : taille d’un conteneur multidimensionnel  restrict(amp, cpu) : restreint la syntaxe C++ pour exécuter une méthode ou une lambda en mode noyau  parallel_for_each : exécute une boucle en parallèle sur des données multidimensionnelle  class array_view<T,N> : wrapper multidimensionnel sur des données  class accelerator : accélérateur utilisé en interne pour exécuter le code parallèle  class accelerator_view : isole l’accélérateur des différents usages multithreads  class array<T,N> : conteneur multidimensionnel sur des données Résumé de la première partie
  • 25.
    Une vue dumatériel GPU pour un développeur Thread … … … …… … …… Registres par Threads N’est pas montré: • Mémoire constante • Mémoire des Mémoire Globale contrôleurs • Schedulers • Autres caches • Cas Multi-GPU Comprendre le calcul tuilé
  • 26.
    Une vue dumatériel GPU pour un développeur … … Tuile composée de threads … …… … …… Registres par Threads Registres par Threads N’est pas montré: • Mémoire constante • Mémoire des Mémoire Globale contrôleurs • Schedulers • Autres caches • Cas Multi-GPU Comprendre le calcul tuilé
  • 27.
    Une vue dumatériel GPU pour un développeur … … Tuile composée de threads … …… … …… Les variable tile_static sont partagée par les threads dans la même tuile Registres par Threads Registres par Thread Cache Programmable Cache Programmable N’est pas montré: • Mémoire constante • Mémoire des contrôleurs • Schedulers Mémoire Globale • Autres caches • Cas Multi-GPU Comprendre le calcul tuilé
  • 28.
    parallel_for_each: surcharge pourles tuiles • Orchestrer des threads array_view<int,1> data(12, my_data); dans des tuiles – La version de base ne le permet pas parallel_for_each(data.extent, • parallel_for_each version [=] (index<1> idx) restrict(amp) tuile accepte ces { … }); surcharges – tiled_extent<D0> ou tiled_extent<D0, D1> ou parallel_for_each(data.extent.tile<6>(), tiled_extent<D0, D1, D2> • L’expression lambda [=] (tiled_index<6> t_idx) restrict(amp) accepte { … }); – tiled_index<D0> ou tiled_index<D0, D1> ou tiled_index<D0, D1, D2> Comprendre le calcul tuilé
  • 29.
    tiled_extent (depuis unextent) extent<1> e(12); 0 1 2 3 4 5 6 7 8 9 10 11 tiled_extent<6> t_e = e.tile<6>(); 0 1 2 3 4 5 6 7 8 9 10 11 extent<2> ee(2, 6); tiled_extent<2, 2> t_ee = ee.tile<2, 2>(); 0,0 0,1 0,2 0,3 0,4 0,5 0,0 0,1 0,2 0,3 0,4 0,5 1,0 1,1 1,2 1,3 1,4 1,5 1,0 1,1 1,2 1,3 1,4 1,5 Techniques plus avancées: calcul tuilé
  • 30.
    tiled_index • Soit col 0 col 1 col 2 col 3 col 4 col 5 array_view<int,2> data(2, 6, p_my_data); row parallel_for_each( 0 data.extent.tile<2,2>(), [=] (tiled_index<2,2> t_idx)… { … }); row 1 T • Lorsque la lambda est exécutée par T – t_idx.global // index<2> (1,3) – t_idx.local // index<2> (1,1) – t_idx.tile // index<2> (0,1) – t_idx.tile_origin // index<2> (0,2) Comprendre le calcul tuilé
  • 31.
    tile_static • Le mot clef tile_static dédié au stockage mémoire • Second mot clef ajouté au langage C++ • Représente la mémoire locale du GPU • Usage du mot clé tile_static – Dans une boucle parallel_for_each tuilée – Le mot clef tile_static devant une déclaration de variable locale place cette variable dans la mémoire locale GPU pour la tuile courante – Cette mémoire est partagée par tous les threads associés à la tuile – Applicable seulement pour les fonctions restrict(amp) Comprendre le calcul tuilé
  • 32.
    tile_barrier • classe tile_barrier – Synchronise tous les threads au sein d’une tuile jusqu'à ce que tous les accès à la mémoire soient terminés • t_idx.barrier.wait(); • ~ t_idx.barrier.wait_with_all_memory_fence(); – Synchronise tous les threads au sein d’une tuile jusqu'à ce que tous les accès à la mémoire globale soient terminés • t_idx.barrier.wait_with_global_memory_fence(); – Synchronise tous les threads au sein d’une tuile jusqu'à ce que tous les accès à la mémoire la mémoire tile_static (locale) soient terminés • t_idx.barrier.wait_with_tile_static_memory_fence(); Comprendre le calcul tuilé
  • 33.
    Cacher avec tile_static 0,0 0,1 0,2 0,3 0,4 0,5 1,0 1,1 1,2 1,3 1,4 1,5 1 static const int TS = 2; 2 array_view<int, 2> av(2, 6, my_vector); 3 parallel_for_each(av.extent.tile<TS,TS>(), [=](tiled_index<TS,TS> t_idx) restrict(amp) 4 { 5 tile_static int t[TS][TS]; 6 t[t_idx.local[0]][t_idx.local[1]] = av[t_idx.global]; 7 8 if (t_idx.local == index<2>(0,0)) { 9 int temp = t[0][0] + t[0][1] + t[1][0] + t[1][1]; 10 av[t_idx.tile_origin] = temp; 11 } 12 }); 13 int sum = av(0,0) + av(0,2) + av(0,4); //the three tile_origins Comprendre le calcul tuilé
  • 34.
    tile_static en action 0,0 0,1 0,2 0,3 0,4 0,5 1,0 1,1 1,2 1,3 1,4 1,5 1 static const int TS = 2; 2 array_view<int, 2> av(2, 6, my_vector); 3 parallel_for_each(av.extent.tile<TS,TS>(), [=](tiled_index<TS,TS> t_idx) restrict(amp) 4 { 5 tile_static int t[TS][TS]; 6 t[t_idx.local[0]][t_idx.local[1]] = av[t_idx.global]; 7 8 if (t_idx.local == index<2>(0,0)) { 9 int temp = t[0][0] + t[0][1] + t[1][0] + t[1][1]; 10 av[t_idx.tile_origin] = temp; 11 } 12 }); 13 int sum = av(0,0) + av(0,2) + av(0,4); //the three tile_origins Comprendre le calcul tuilé
  • 35.
    Synchro avec tile_barrier0,0 0,1 0,2 0,3 0,4 0,5 1,0 1,1 1,2 1,3 1,4 1,5 1 static const int TS = 2; 2 array_view<int, 2> av(2, 6, my_vector); 3 parallel_for_each(av.extent.tile<TS,TS>(), [=](tiled_index<TS,TS> t_idx) restrict(amp) 4 { 5 tile_static int t[TS][TS]; 6 t[t_idx.local[0]][t_idx.local[1]] = av[t_idx.global]; 7 tile_barrier.wait(); 8 if (t_idx.local == index<2>(0,0)) { 9 int temp = t[0][0] + t[0][1] + t[1][0] + t[1][1]; 10 av[t_idx.tile_origin] = temp; 11 } 12 }); 13 int sum = av(0,0) + av(0,2) + av(0,4); //the three tile_origins Comprendre le calcul tuilé
  • 36.
    Retour sur laMultiplication Matricielle • Chargement des données pour 4 threads • Les threads chargent plusieurs fois la même valeur depuis la mémoire globale ! – Comment éviter ce problème ? Comprendre le calcul tuilé
  • 37.
    Multiplication Matricielle –Exemple (tuilée) void MatrixMultSimple(vector<float>& vC, const vector<float>& vA, const void MatrixMultTiled(vector<float>& vC, const vector<float>& vA, const void vector<float>& vB, int M, int N, int W ) vector<float>& vB, int M, int N, W) const vector<float>& vB, int M, int N, int W ) { { static const int TS = 16; const int TS = 16; array_view<const float,2> a(M, W, vA), b(W, N, vB); array_view<const float,2> a(M, W, vA), b(W, N, vB); array_view<float,2> c(M,N,vC); c.discard_data(); array_view<float,2> c(M,N,vC); c.discard_data(); parallel_for_each(c.extent, parallel_for_each(c.extent.tile< TS, TS >(), TS, TS >(), [=] (index<2> idx) restrict(amp) { [=] (tiled_index<TS, TS> t_idx) restrict(amp) { { (tiled_index< TS, TS> t_idx) restrict(amp) int row = idx[0]; int col = idx[1]; int row = t_idx.local[0]; int col = t_idx.local[1]; t_idx.local[1]; float sum = 0.0f; float sum = 0.0f; for (int i = 0; ii < W; ii += TS) {{ 0; < W; += TS) tile_static float locA[TS][TS], locB[TS][TS]; tile_static float locA[TS][TS], locB[TS][TS]; Phase 1 locA[row][col] = a(t_idx.global[0], col i); locA[row][col]= a(t_idx.global[0], col ++ i); locB[row][col] = b(row + i, t_idx.global[1]); locB[row][col] = b(row + i, t_idx.global[1]); t_idx.barrier.wait(); t_idx.barrier.wait(); for(int k = 0; k < W; k++) for (int k = 0; k < TS; k++) Phase 2 sum += a(row, k) * b(k, col); sum += locA[row][k] * locB[k][col]; t_idx.barrier.wait(); t_idx.barrier.wait(); } c[idx] = sum; c[t_idx.global] = sum; c[t_idx.global] = sum; } ); } ); } } Techniques plus avancées: calcul tuilé
  • 38.
    Démo Produit MatricielTuilé ! Comprendre le calcul tuilé
  • 39.
    C++ AMP ParallelDebugger • Fonctionnalités bien connues du débogage Visual studio – Launch, Attach, Break, Stepping, Breakpoints, DataTips – Outillage orienté fenêtre • Processes, Debug Output, Modules, Disassembly, Call Stack, Memory, Registers, Locals, Watch, Quick Watch • Nouvelles fonctionnalités (à la fois le CPU et le GPU) – Parallel Stacks window, Parallel Watch window, Barrier • Nouveautés spécifiques GPU – Emulateur, fenêtre GPU Threads , détection de races conditions Visual Studio
  • 40.
    Debugger GPU enAction Visual Studio
  • 41.
    2ième Partie: ceque vous avez appris ! Comprendre le calcul tuilé  La macro architecture GPU pour les développeurs  La classe tile_static pour cacher des données pour un groupe de threads  Le classe tiled_extent< , , > permet à la surcharge parallel_foreach de simplifier le calcul en tuilé  La classe tiled_index< , , > permet à la surcharge parallel_foreach de simplifier le calcul en tuilé  La classe tile_barrier permet de synchroniser les threads au sein d’une tuile Mise au point avec Visual Studio  Configurer le mode Debug GPU  Détecter les races conditions  Utiliser la fenêtre Parallel GPU pour observer l’état de tous les threads et naviguer entre eux  Utiliser la fenêtre Parallel Watch pour observer des données pour chacun des threads  Exécuter toute une tuile de threads jusqu’à votre curseur Résumé de la seconde partie
  • 42.
    Conclusion • Démocratisation de la programmation parallèle hétérogène – Haute performance accessible au grand public – Abstraction de la plateforme matérielle – Abstractions de haut niveau en C++ (pas en langage C) – Repose sur un jeu minimal d’API – C++ AMP est une spécification libre – Etat de l’art de l’IDE de Visual Studio C++ AMP en quelques mots
  • 43.
    Pour aller plusloin … MSDN Forums to ask questions • http://social.msdn.microsoft.com/Forums/en/parallelcppnative/threads MSDN Native parallelism blog (team blog) • http://blogs.msdn.com/nativeconcurrency/ Libraries for C++ AMP C++ AMP Samples • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/05/19/libraries-for- • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/01/30/c-amp-sample- c-amp.aspx projects-for-download.aspx Graphics in C++ AMP C++ AMP for the OpenCLProgrammer • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/01/25/concurrency- • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/04/10/c-amp-for- graphics-in-c-amp.aspx the-opencl-programmer.aspx Interoperability between Direct 3D and C++ AMP C++ AMP for the CUDA Programmer • http://blogs.msdn.com/b/nativeconcurrency/archive/2011/12/29/interoperabilit • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/04/11/c-amp-for- y-between-direct-3d-and-c-amp.aspx the-cuda-programmer.aspx C++ AMP open spec C++ AMP for the DirectCompute Programmer • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/02/03/c-amp-open- • http://blogs.msdn.com/b/nativeconcurrency/archive/2012/04/09/c-amp-for- spec-published.aspx the-directcompute-programmer.aspx Pour creuser un peu plus …
  • 44.
    Développeurs Pros de l’IT http://aka.ms/generation-app Formez-vous en ligne www.microsoftvirtualacademy.com http://aka.ms/evenements- developpeurs Retrouvez nos évènements http://aka.ms/itcamps-france Les accélérateurs Faites-vous accompagner Windows Azure, Windows Phone, gratuitement Windows 8 Essayer gratuitement nos http://aka.ms/telechargements solutions IT La Dev’Team sur MSDN Retrouver nos experts L’IT Team sur TechNet http://aka.ms/devteam Microsoft http://aka.ms/itteam

Notes de l'éditeur

  • #2 Notation
  • #3 Intro code / dev
  • #41 Utilisation de Visual C++ component extensions (C++/CX ) afin de produire un application Windows Store sur le base de l’application full C++.