Barra : un simulateur de GPU pour CUDA Sylvain Collange ([email protected]) Qu’est-ce qu’un simulateur ? Les simulateurs fonctionnels émulent le comportement d’un processeur, indépendants de l’implémentation, ou micro-architecture. Ils permettent de : – concevoir et tester des jeux d’instructions, – générer des traces d’exécution, – tester et le déboguer des logiciels. Les simulateurs niveau transaction et niveau cycle simulent une microarchitecture donnée suivant un modèle de temps, voire de consommation. Ils permettent : – d’instrumenter et d’optimiser les logiciels, – d’identifier les goulets d’étranglement d’une micro-architecture, – de quantifier l’impact de modifications micro-architecturales, – de borner des temps d’exécution (WCET). . . Simulateurs existants – CPU : SimpleScalar, ooosim. . . – GPU : Attila, Qsilver. Un GPU : NVIDIA G80 Spécificités – SIMD large, – multithreading matériel massif, – exécution transparente des branchements par prédication, – accès mémoire par Gather et Scatter, – mémoire hétégogène : constante, locale, globale. . . Ces aspects ne sont pas pris en charge par les simulateurs existants. L’architecture n’est pas documentée en détail. Il est nécessaire d’effectuer des tests pour en comprendre le fonctionnement [1, 4]. Nous nous intéressons à l’architecture N VIDIA : G80, G92 et GT200 et à son jeu d’instruction [5]. 31 24 16 8 Les instructions sont complexes mais décomposables en sous-éléments orthogonaux : prédicat, opération, destination, sources. Chaque sous-élément est décodé indépendamment. Warp 3 : @p1.leu mad.f32.rn p1|r2, s[a2+48], r0, c14[32] Ordonnancement Fetch Décodage Lecture opérandes Ordonnanceur masque Masque Exécution & 32 mad.f32.rn @p1.leu Pred W1 W2 W3 W4 @p1.leu 32 Numéro de warp OP PC mad.f32.rn c14[32] 32x32 .leu s[a2+48] Warp 3 W24 Dest p1|r2 Src1 s[a2+48] Src2 r0 Src3 c14[32] @p1 r0 ALU/ FPU 4x32 Registres p1 prédicat Registres adresse Décodeurs 64 ID de warp Addresse Vecteur Masque Écriture 4x32 r2 32x32 32x32 Registres généraux 32x32 a2 Mémoire Intégration avec CUDA Un pilote virtuel sous la forme d’une bibliothèque dynamique remplace la bibliothèque N VIDIA CUDA et intercepte les appels de fonctions de l’application ou de la couche Runtime haut-niveau de CUDA. Aucune recompilation n’est nécessaire. Des drapeaux permettent d’activer la génération de traces. Application Application CUDA Runtime 0 CUDA Runtime Pilote CUDA Pilote Barra GPU Simulateur de GPU Mot 0 OP Adress reg[0-1] Adress Imm/reg Src3 from const mem Src2 from const mem Src2 Src1 Dest Flow control Long instruction Résultats 31 24 16 8 0 Mot 1 – Simulation correcte de la plupart des programmes du SDK CUDA. – Vitesse d’exécution comparable à celle de l’émulation niveau source. SubOP Src3 neg Src1 neg Src2 imm GPU execution (9800GX2) Barra CUDA emulation 100,000 Rounding mode Src1 from sh mem Src3 Pred reg Pred cond Set pred Set pred reg Dest is output reg Adress reg[2] Marker Execution time 10,000 Simuler le G80 1,000 100 10 Les registres et les opérateurs sont vectoriels. Les branchements sont simulés par prédication implicite à l’aide d’une pile de masque et de plusieurs piles d’adresses. Code C if(p) { ... } else { ... } Assembleur @!p br else Pile cibles push(else) PC = pop() push(endif) push(p) pop() transpose scanLargeArray reduction matrixMul histogram256 – Validation de propositions de modifications architecturales [2]. – Simulation niveau transaction de la mémoire et des cœurs d’exécution. – Modèle de consommation. – Débogueur. p = pop() push(p̄∧top) Références endif ... endif: Travaux futurs Pile masques else ... br endif else: fastWalshTransform mov.b32 s[a1+0x0030], r11 mov.b32 s[a1+0x0070], r0 bar.sync 0 mov.u32.u32.rd r0, s[a3+0x0000] r0 = (0.794227, 0.115912, . . .) mad.f32.rn r2, s[a2+0x0030], r0, r2 r2 = (0.309071, 0.045107, . . .) mov.u32.u32.rd r0, s[a3+0x0010] r0 = (0.157347, 0.436301, . . .) MonteCarlo 0xe422c78004001801 0xe420078004003801 0x00000000861ffe03 0x0423c7801c00c001 0x00208780e800d809 0x0423c7801c00c801 0 MersenneTwisterBM Un émulateur du jeu d’instruction N VIDIA G80 décode et exécute les instructions natives. MersenneTwister 1 pop() Le simulateur exécute alternativement les instructions de plusieurs threads (warps) suivant un ordonnancement round-robin. Ces warps sont synchronisables par une instruction barrière. Les instructions d’accès mémoire Gather et Scatter acceptent des vecteurs d’adresses. Les bancs de registres sont implicitement indexés par le numéro de warp courant. Les instructions sont prédiquées à la fois de manière explicite par un registre de prédicat et de manière implicite par le masque de branchement courant. [1] Sylvain Collange, Marc Daumas, and David Defour. État de l’intégration de la virgule flottante dans les processeurs graphiques. RSTI-TSI, VOL 27/6:719–733, 2008. [2] Sylvain Collange, Marc Daumas, David Defour, and Régis Olivès. Fonctions élémentaires sur GPU exploitant la localité de valeurs. In Actes de SYMPA, 2008. http://hal.archivesouvertes.fr/hal-00202906. [3] Sylvain Collange, David Defour, and David Parello. Barra, a Modular Functional GPU Simulator for GPGPU. http://hal.archives-ouvertes.fr/hal-00359342, 2009. [4] Sylvain Collange, David Defour, and Arnaud Tisserand. Power Consuption of GPUs from a Software Perspective. http://hal.archives-ouvertes.fr/hal-00348672, 2009. [5] Wladimir J. van der Laan. Decuda, cudasm, 2007. http://www.cs.rug.nl/~wladimir/decuda/.