Sortie de Nim 0.12

Nim (connu, jusqu’il y a peu, sous le nom de Nimrod) est un langage de programmation « moderne » (comme c’est à la mode) aussi performant que le C (souvent indiqué comme une référence à ce niveau) : de manière générale, il souhaite donner un maximum de puissance à l’utilisateur sans sacrifier la performance (comme Rust ou Julia). Au niveau de ses caractéristiques, son système de typage est statique et son style impératif, bien que le langage soit multiparadigme : il mélange métaprogrammation, fonctionnel, passage de messages, procédural et orienté objet, avec un ramasse-miettes. Il ne dispose pas d’un compilateur, mais d’un transcompilateur, qui produit du code C, C++ ou Objective-C en sortie… ou encore en JavaScript, ce qui facilite l’intégration avec l’environnement extérieur (il est aussi très facile d’appeler des fonctions externes C).

De manière générale, la syntaxe rappelle Python (pour la délimitation des blocs) et Scala (pour les types et certaines syntaxes raccourcies), avec des types, mais aussi le renvoi implicite d’une variable (ce qui contribue à raccourcir le code… avec un peu d’habitude) :

proc reverse(s: string): string =
  result = ""
  for i in countdown(high(s), 0):
    result.add s[i]

var str1 = "Reverse This!"
echo "Reversed: ", reverse(str1)

La métaprogrammation est poussée plus loin qu’en C++ : elle permet de générer n’importe quel type de code à la compilation, en travaillant directement au niveau de l’AST du compilateur. Par exemple, la fonction suivants génère des fonctions dont le nom dépend des arguments. (Bien évidemment, ce système peut être utilisé dans des cas plus complexes.)

import macros

template abcProc(n: expr): stmt  =  
  proc n() = echo astToStr(n)

abcProc(A)
abcProc(B)
abcProc(C)

L’un des concepts principaux derrière le langage concerne les aspects parallèles : chaque fil d’exécution peut communiquer avec les autres à l’aide de messages asynchrones, des canaux. Le ramasse-miettes peut donc fonctionner indépendamment pour chaque fil d’exécution : même en en mettant un en pause, l’application peut toujours fonctionner ; ce système est donc adapté aux applications qui ne sont pas strictement temps réel, comme les jeux (mais contrairement à une bonne partie de l’électronique). Cependant, ces mécanismes sont encore expérimentaux.

Cette nouvelle version apporte pas mal de changements qui cassent la rétrocompatibilité, comme l’envoi d’exceptions lors de l’accession à une clé non existante dans une table de hachage (au lieu d’un null). La grande nouveauté est cependant NimScript, un sous-ensemble très léger du langage qui peut être exécuté dans la machine virtuelle incluse, déjà notamment utilisée pour l’évaluation de fonctions lors de la compilation ; maintenant, ce système peut également être utilisé de manière plus générale, notamment pour remplacer le système de configuration existant. Par contre, l’accès au code C n’est pas encore possible, ce qui limite fortement les possibilités, mais est largement suffisant pour une description d’un paquet ou pour des scripts d’installation et de compilation.

Sources : Nim version 0.12.0 released, Nim (programming language), Nimrod: A New Systems Programming Language, A cursory look at meta-programming in Nim.

Sortie de NVIDIA Nsight Visual Studio Edition 5.0

NVIDIA Nsight est une extension à Visual Studio (ou Eclipse) lui ajoutant diverses fonctionnalités dans le domaine du calcul sur GPU au sens large : l’outil est prévu pour des technologies de calcul pur (CUDA, OpenCL, DirectCompute) et de rendu 3D (Direct3D, OpenGL).

Cette nouvelle version, estampillée 5.0, est maintenant compatible avec Visual Studio 2015 (sauf pour les applications CUDA 7.5 !). Côté graphique, le débogage fonctionne également avec Direct3D 12, même si la majorité des fonctionnalités habituelles ne sont pas encore disponibles pour cette version (principalement celles axées sur l’analyse de la performance), mais devraient l’être dans une prochaine version. Les applications OpenG 4.4 et 4.5 sont aussi mieux gérées.

Pour le calcul proprement dit, les instantanés produits par le pilote en cas de plantage peuvent être ouverts dans Visual Studio pour une analyse plus détaillée. Les outils d’analyse sont désormais compatibles avec OpenCL 1.2. (NVIDIA étant largement en retard à ce niveau, AMD ayant déjà publié des pilotes compatibles OpenCL 2.0) Il devient également possible de déboguer et de profiler des applications CUDA distantes (à condition d’utiliser CUDA 7.5). Les systèmes d’exploitation 32 bits ne sont plus pris en charge et les processeurs de génération Fermi (GeForce 400/500, distribués jusqu’en 2012) ne le seront plus à la prochaine version.

Sources : Nsight Visual Studio Edition 5.0 New Features , Release Notes.

Sortie de GSL 2.0

La GNU Scientific Library est une bibliothèque de fonctions en C qui s’occupe de calcul numérique : elle fournit des générateurs de nombres aléatoires, des fonctions spéciales (fonctions de Bessel, intégrales elliptiques, polynômes de Laguerre généralisés, etc.), une implémentation de BLAS pour l’algèbre linéaire, etc. La version précédente, numérotée 1.16, est sortie en juillet 2013.

GSL 2.0 apporte des nouveautés significatives, notamment au niveau des matrices creuses, dont un solveur de systèmes linéaires utilisant l’algorithme GMRES. Le module de régression aux moindres carrés peut maintenant utiliser une formulation robuste, avec une régression d’arête (aussi dite de Tychonoff) ; de même, de nouvelles fonctions s’occupent de la régression non linéaire, comme lmniel pour des systèmes avec beaucoup de données. Pour l’interpolation, de nouvelles techniques 2D ont été implémentées : l’interpolation bilinéaire et bicubique.

La version majeure a augmenté suite à certains changements plus profonds : l’API a légèrement évolué pour certaines fonctions, d’autres ont été supprimées ; l’architecture interne de la bibliothèque a également évolué, de telle sorte que la compatibilité binaire n’est plus assurée (pour profiter de cette nouvelle version, les binaires existants devront être recompilés).

Source : GNU Scientific Library 2.0 released.

OpenMP 4.1 sera nommé 4.5

Le premier brouillon d’OpenMP 4.1 a été publié en juillet et son implémentation a déjà débuté côté GCC. Un récent message sur la liste de diffusion du compilateur indique que cette nouvelle version aura pour numéro 4.5, à cause des changements importants apportés au niveau de la spécification. La rétrocompatibilité sera cependant parfaite avec le code OpenMP 4.0. La version finale est toujours attendue pour la conférence SC15 (supercomputing 2015, du 15 au 20 novembre).

Les fonctionnalités principales attendues pour cette prochaine révision sont la définition de priorités pour les tâches, une première API pour l’affinité de la mémoire et une meilleure compatibilité avec Fortran 2003 (seules dix limitation sont encore listées dans la norme). En outre, les références en C++ sont mieux gérées, les différentes itérations d’une boucle pourront être réparties entre des tâches différentes (directive taskloop) et les dépendances pourront être indiquées.

Plus spécifiquement, pour les dépendances, les directives utilisant la construction ordered (utilisée pour indiquer que les itérations de la boucle doivent avoir lieu de manière séquentielle dans une section parallélisée) pourront prendre un paramètres pour indiquer le nombre de boucles associées à cette directive. Une clause simd limitera le bloc à une unité d’exécution SIMD.

Le travail sur OpenMP 5.0 commence déjà. La sortie est prévue en 2017 (une estimation déjà dite optimiste), avec au menu une meilleure compatibilité avec les versions les plus récentes de C, C++ et Fortran (C11, C++14, Fortran 2003), une interopérabilité avec pthreads, mais aussi la mémoire transactionnelle ou du parallélisme en dehors des régions déclarées parallel.

Sources : OpenMP 4.1 is dead, long live OpenMP 4.5, OpenMP 4.1 Morphs Into OpenMP 4.5, OpenMP 4.1 and 5.0 Progress, OpenMP 4.1 TR3.

Sortie de Julia 0.4

À peine un mois après la sortie de sa RC 1, plus d’un an après sa dernière version mineure (0.3 en août 2014), la version finale de Julia 0.4 est sortie. Ce langage de programmation assez récent est principalement prévu pour le calcul scientifique. Similaire à MATLAB au niveau de la syntaxe, il est de très haut niveau et dynamique, tout en atteignant une performance comparable aux langages statiques comme C ou Fortran.

Pour les utilisateurs, les nouveautés principales de cette version mineure concernent la performance : la compilation incrémentale des paquets, ce qui réduit fortement le temps de chargement des plus lourds d’entre eux ; le ramasse-miette est passé à une implémentation par générations, ce qui améliore la performance dans les cas les plus fréquents par rapport à l’implémentation traçante précédente ; des canaux de communication entre tâches plus rapides. Les types des tuples ont complètement changé, passant de (A,B) à Tuple{A,B}, ce qui est plus cohérent ; de plus, cette refactorisation autorise la graphie field::NTuple{N,T} pour désigner des tuples de N éléments d’un même type.

Les développeurs de paquets se réjouiront de la finalisation d’autres fonctionnalités, comme la surcharge des appels de fonction (y compris les constructeurs) pour n’importe quel objet (et pas seulement la fonction elle-même) ; en particulier, il devient possible de définir des constructeurs pour des types abstraits. Les fonctions générées donnent un contrôle bien plus grand sur la spécialisation des fonctions lors de la compilation : ces fonctions retournent un code en fonction du type des paramètres (et non une valeur en fonction des valeurs des paramètres pour les fonctions traditionnelles).

De manière générale, Julia 0.4 a aussi été l’occasion d’améliorer la performance (réduction des vérifications effectuées lors de l’adressage tout en garantissant le même niveau de sécurité) et la flexibilité des tableaux à plusieurs dimensions, des vues sur ces tableaux (SubArray) et de l’itération (avec par exemple la fonction eachindex() pour itérer sur tous les éléments d’un tableau, peu importe le nombre de dimensions, en garantissant une performance optimale) ; les changements implémentés sont listés dans un métaticket. Plus de changements sont prévus pour Julia 0.5, détaillés dans le métaticket Arraypocalypse.

La communauté Julia a aussi gagné en maturité depuis l’instant de mise à disposition de la version 0.3. Notamment, une série de paquets ont atteint une belle maturité : JuliaOpt pour l’optimisation ; JuliaStats pour les statistiques et l’apprentissage automatique ; JuliaGPU pour l’accès au GPU ; IJulia, une interface en feuilles de calcul basée sur IPython ; Images.jl pour le traitement d’images ; Gadfly et Winston pour les graphiques ; Juno comme environnement de développement.

Source : Julia 0.4 Release Announcement.

Merci à Claude Leloup pour ses corrections.

Sortie de Julia 0.4 RC1

Julia est un langage de programmation assez récent prévu pour le calcul scientifique (sa première version est sortie en 2009). Similaire à MATLAB au niveau de la syntaxe, il est de très haut niveau et dynamique, mais sa conception même lui permet d’atteindre une performance comparable aux langages statiques comme C ou Fortran, par l’utilisation de la compilation juste à temps (JIT). Selon les tests de performance publiés sur le site officiel, il peut être plus rapide que le C ou jusque deux fois plus lent — son principal concurrent, MATLAB, pouvant être plusieurs milliers de fois plus lent. Pour y arriver, l’interpréteur n’utilise pas la programmation orientée objet, mais un concept dérivé : la fonction à appeler est déterminé en fonction du type des arguments (multiple dispatch) ; au besoin, il peut aussi directement appeler du code C ou Fortran, directement depuis la bibliothèque standard (des modules externes permettent de faire la même chose pour C++ et Python).

En tant que langage spécifiquement prévu pour le calcul scientifique, un bon nombre de bibliothèques sont d’ores et déjà disponibles dans le domaine pour former une bibliothèque standard plus que conséquente, principalement pour l’algèbre linéaire, la génération de nombres aléatoires et le traitement du signal. Notamment, toutes les fonctions de BLAS existent, mais sont également intégrées dans la syntaxe du langage (* pour la multiplication matricielle, \ pour la résolution de systèmes linéaires, ⋅ pour le produit scalaire, × pour le produit vectoriel, etc.). Ces opérateurs utilisent les symboles habituels dans la littérature (de même, tout caractère Unicode peut être utilisé comme nom de variable).

Contrairement à bon nombre de langages, le parallélisme est pris en charge directement dans la syntaxe du langage. Par exemple, pour mener un grand nombre d’expériences de pile ou face sur des grappes de calcul, le code est extrêmement simple :

nheads = @parallel (+) for i=1:100000000
  int(randbool())
end

Parmi les fonctionnalités très attendues de cette nouvelle version 0.4 RC1, la précompilation évite de compiler les modules importés à chaque exécution d’un script : ils sont convertis en code binaire une fois pour toutes, ce qui améliore fortement les temps de chargement (dans certains cas, d’une dizaine de secondes à moins d’une). De plus, cette fonctionnalité ne réduit pas la performance du code à l’exécution : le même moteur de compilation est utilisé (LLVM), il est seulement moins souvent appelé.

La documentation des modules pour la version 0.3 se faisait par le module Docile.jl et est maintenant intégrée directement au niveau du langage. Le texte écrit en Markdown et est accessible depuis l’invite de commande. Cette fonctionnalité est incrémentale, de telle sorte que, si un fichier est modifié, seules les méthodes ayant subi des changements seront recompilées.

Un autre point fort du langage est la métaprogrammation, avec des macros très similaire à l’esprit de LISP : ces macros peuvent prendre du code en argument, représenté comme une structure de données (arbre syntaxique abstrait), manipulable à l’envi, afin de générer du code à l’exécution. Ces fonctionnalités sont très pratiques pour développer des langages dédiés (comme JuMP pour la programmation mathématique), intégrés directement dans le code Julia. À ce sujet, cette version 0.4 utilise la métaprogrammation pour définir des fonctions générées : son implémentation travaille uniquement sur le type des variables, la valeur retournée étant l’expression à évaluer lors de l’appel effectif de cette fonction sur des valeurs données — sans que le code qui appelle cette fonction soit conscient de ces détails.

La version finale de Julia 0.4 est attendue assez rapidement, une fois les derniers défauts corrigés. Les nouvelles fonctionnalités seront ajoutées dans la branche Julia 0.5, qui pourrait également apporter quelques changements qui casseront la compatibilité avec le code existant. Une nouveauté attendue sera la gestion du débogage interactif (actuellement implémenté dans un module externe, qui nécessite d’instrumenter le code : Debug.jl).

Source : Julia v0.4.0 Release Notes.

AMD se lance dans le HPC

Malgré quelques superordinateurs utilisant ses processeurs Opteron (notamment, le deuxième le plus puissant), d’autres plus nombreux ses processeurs graphiques Firepro, AMD est loin d’être une figure proéminente du HPC. Dans un article paru dans la revue IEEE Micro (Achieving Exascale Capabilities through Heterogeneous Computing), le fondeur semble vouloir prendre sa revanche, avec une surenchère sur les annonces récentes d’Intel.

Leur prochain APU (accelerated processing unit), mêlant des parties CPU et GPU, sera nommé EHP, pour exascale heterogeneous processor. En peu de mots, de processeur devrait contenir trente-deux cœurs CPU, plusieurs milliers de cœurs GPU avec une mémoire jusqu’à trente-deux gigaoctets, directement intégrée à la puce. Là où Intel prévoit d’utiliser la technologie HMC, AMD profite de son partenariat avec SK Hynix et intègre HBM2. Ce processeur très spécifique pourrait être disponible en 2016-2017 au plus tôt.

Cette nouvelle puce est exclusivement prévue pour le monde des supercalculateurs, où la course à l’exaflops (un milliard de milliards d’opérations en virgule flottante, avec une précision de soixante-quatre bits) est lancée. La solution conçue par AMD propose des superordinateurs s’étendant sur cent mille nœuds de calcul, chacun fournissant de l’ordre de dix teraflops de puissance de calcul : en comparaison, le superordinateur le plus puissant actuellement, Tianhe-2, s’étend sur seize mille nœuds, chacun fournissant approximativement deux téraflops.

Une telle machine nécessiterait cependant encore beaucoup de recherche, notamment au niveau de l’interconnexion des nœuds, mais également de la consommation énergétique : Tianhe-2 a besoin d’un peu moins de vingt-cinq mégawatts en comptant le refroidissement, un quarantième de la production d’un réacteur nucléaire moyen (utiliser le même système pour atteindre l’exaflops nécessiterait tout un réacteur !). Les projections actuelles font état d’une efficacité de l’ordre de quarante à cinquante gigaflops pare watt pour les nouvelles puces AMD.

Un nouveau paradigme est donc requis. Il est très similaire à celui déjà utilisé : complémenter les CPU avec des accélérateurs. AMD propose d’intégrer ces accélérateurs directement sur la même puce que le processeur principal, afin notamment de partager la même mémoire et de ne pas être limité par les bus disponibles, y compris à l’intérieur d’un nœud de calcul — les déplacements de données forment également une bonne partie de la consommation énergétique d’un supercalculateur. D’autres avantages incluent une meilleure densité de performance, mais également l’absence de recours à un système d’exploitation pour une série de tâches (une tâche exécutée sur le GPU pourrait lancer de nouvelles tâches sur le GPU ou le CPU sans nécessiter d’appel aux pilotes ou au système d’exploitation). Les parties CPU pourraient s’occuper des éléments de calcul difficiles à paralléliser, c’est-à-dire les parties qui ne peuvent pas être lancées efficacement sur les accélérateurs.

Cependant, AMD ne propose pas d’intégrer tous les composants dans la puce elle-même : notamment, elle disposerait de huit canaux DDR4 pour l’accès à de la mémoire supplémentaire, ajoutée sous la forme de barrettes plus traditionnelles, jusqu’à atteindre les besoins actuels en mémoire. Chaque canal pourrait adresser jusqu’à deux cent cinquante-six gibioctets de mémoire (c’est-à-dire deux tébioctets en tout). Au niveau de l’architecture globale, chaque cœur aurait des deux fils d’exécution du cœur, avec un cache cache L2 d’un demi-kibioctet ; chaque groupe de quatre cœurs partagerait un cache L3 de huit mébioctets. La conception globale pourrait s’accommoder de cœurs x86 ou ARM.

Au vu de ces caractéristiques, il semble peu probable, au vu des techniques de lithographie actuelles, que tout ce petit monde tienne sur la même pastille : le processeur serait séparé en deux parties, chacune ayant seize cœurs, plus la partie GPU, rassemblées par un interposeur, d’ores et déjà nécessaire pour intégrer la mémoire HBM2.

Sources : AMD Reveals the Monsterous ‘Exascale Heterogeneous Processor’ (EHP) with 32 x86 Zen Cores and Greenland HBM2 Graphics on a 2.5D Interposer, AMD’s Exascale Strategy Hinges on Heterogeneity, AMD announces EHP (exascale heterogeneous processor), High performance AMD APU

La nouvelle mouture de l’Intel Xeon Phi approche

Pour contrecarrer le mouvement des processeurs graphiques (GPU) dans le domaine du calcul scientifique de haute performance (HPC), Intel a lancé en 2013 ses coprocesseurs Xeon Phi. L’idée principale est de fournir un grand nombre de cœurs (une soixantaine pour la première génération), atout principal des GPU (les modèles actuels montent à plusieurs milliers de cœurs). La première génération de Xeon Phi est notamment utilisée dans le supercalculateur chinois Tianhe-2, le plus puissant au monde depuis sa construction en 2013, selon le classement Top500.

Cependant, comme pour les GPU, ces cœurs sont très différents de ceux des processeurs plus traditionnels (comme les gammes Core i3-7 d’Intel, par exemple) : ils sont nettement moins véloces, avec des fréquences de l’ordre du gigahertz — le parallélisme de ces coprocesseurs est leur principale caractéristique. Néanmoins, contrairement aux GPU, ils peuvent se programmer d’une manière très similaire aux processeurs classiques.

La deuxième génération est sur le point d’arriver, elle devrait être commercialisée cette année. Connue sous le nom de code Knights Landing, cette nouvelle mouture tient du superlatif pour ses caractéristiques : pas moins de septante-deux cœurs, chacun hébergeant deux unités de traitement vectoriel AVX-512 (qui travaillent sur des registres de cinq cent douze bits) ; individuellement, ces cœurs devraient être trois fois plus rapides que précédemment. La puce elle-même est gravée avec un processus à quatorze nanomètres, ce qui se fait de mieux pour le moment, avec notamment de grands gains en termes de puissance consommée (et de chaleur dissipée).

L’architecture prévoit aussi des bus DDR4 pour la mémoire principale, mais surtout seize gigaoctets de mémoire intégrée sur la puce elle-même (avec une bande-passante jusqu’à cinq fois supérieure à celle de la DDR4, quinze fois par rapport à la DDR3, encore majoritaire dans les ordinateurs actuels). Cette mémoire sera à peu près aussi rapide que les caches L3 des processeurs actuels (bien qu’ils soient limités à quelques mégaoctets !), avec une bande passante de cinq cents gigaoctets par seconde.

Cette super-mémoire cache utilisera l’interface HMC (hybrid memory cube), concurrente de HBM (high bandwidth memory) en cours de déploiement sur les processeurs graphiques (les cartes Fury remettent AMD à l’avant-plan en termes de performance). In fine, un tel processeur pourra proposer une puissance de calcul de l’ordre de trois téraflops, c’est-à-dire trois mille milliards d’opérations en virgule flottante par seconde.

Une autre nouveauté par rapport à la génération précédente est que ces coprocesseurs pourront servir de processeurs principaux et faire fonctionner le système d’exploitation de la machine. L’intérêt majeur est de se débarrasser d’une connexion par bus PCI-Express, qui limitait la performance de son prédécesseur (une constatation partagée par le concurrent NVIDIA, qui s’apprête à proposer sa technologie NVLink).

Au niveau de la communication, Intel s’apprête également à communiquer plus d’informations sur son Omni Path, une interface d’interconnexion entre nœuds dans un supercalculateur. Intel s’apprête ainsi à marcher sur les platebandes de géants comme InfiniBand, avec une technologie présentée comme supérieure. Notamment, les latences de communication devraient être de l’ordre de cent nanosecondes, quand InfiniBand monte à presque deux microsecondes, Ethernet à dix à cent microsecondes.

Chaque processeur Xeon Phi Knights Landing aura deux ports Omni Path. L’objectif semble être l’intégration verticale dans les supercalculateurs, Intel ayant annoncé un partenariat avec HP pour la construction de serveurs autour des technologies HPC d’Intel, rassemblées sous le nom de SSF (scalable system framework), dans le contexte de la gamme Apollo de HP.

Sources et crédit images : Intel’s Xeon Phi 14nm ‘Knights Landing’ Co-Processors Detailed – OmniPath Architecture 100 Series and 16GB HMC on a 2.5D Interposer et Quick Note: Intel “Knights Landing” Xeon Phi & Omni-Path 100 @ ISC 2015.

Sortie de CUDA 7.5 RC

La première préversion publique de CUDA 7.5 est disponible pour tous les développeurs CUDA enregistrés auprès de NVIDIA. La principale nouveauté est la gestion des nombres à virgule flottante codés sur seize bits, c’est-à-dire la moitié de l’encodage traditionnel (float sur trente-deux bits). Cette format est principalement utilisée pour du stockage de données quand la précision requise n’est pas très importante, mais aussi pour des calculs sous la même hypothèse.

Calculs sur seize bits

L’effet sur la performance du code peut être énorme : la bande passante requise est divisée par deux, ce qui permet de transmettre deux fois plus de nombres par unité de temps sur les bus existants, d’en stocker deux fois plus sur la même quantité de mémoire. Ils pourront donc se révéler très utiles pour les applications où ces éléments sont limitatifs, comme l’apprentissage de réseaux neuronaux de grande taille ou le filtrage de signaux en temps réel.

L’avantage en temps de calcul n’est disponible que sur les GPU ayant une partie prévue pour l’arithmétique sur seize bits, ce qui n’est pas le cas pour la majorité des processeurs disponibles, sauf sur Tegra. La prochaine architecture de GPU de NVIDIA, connue sous le nom de Pascal, aura des transistors alloués pour les calculs sur seize bits : le nombre de calculs effectués par seconde doublera entre les précisions FP16 et FP32 (le même rapport qu’entre FP32 et FP64). Entre temps, les calculs seront effectués en interne sur les mêmes circuits que précédemment, avec une précision bien plus élevée que des circuits dédiés (bien qu’elle soit en grande partie perdue lors de l’arrondi vers les seize bits).

Réseaux neuronaux

L’un des chevaux de bataille actuels de NVIDIA est l’apprentissage automatique, en particulier par réseaux neuronaux, plus spécifiquement profonds, par exemple pour une utilisation dans les voitures intelligentes. Ils ont par exemple développé la bibliothèque cuDNN (CUDA deep neural network), qui accélère les calculs par un GPU. La troisième version de cette bibliothèque a été optimisée, particulièrement au niveau des convolutions (FFT et 2D), ce qui améliore la performance lors de l’entraînement de réseaux neuronaux (gain d’un facteur deux pour l’apprentissage sur des GPU Maxwell). Elle gère également les nombres en virgule flottante sur seize bits, ce qui est utile pour des réseaux très grands, mais n’améliore pas (encore) les temps de calcul.

Améliorations de performance de cuDNN 3

NVIDIA a aussi développé l’outil DIGITS, un logiciel de bien plus haut niveau que cuDNN pour les mêmes réseaux neuronaux, prévu pour des profils plus scientifiques que pour des programmeurs. L’une des nouveautés est l’apprentissage distribué sur plusieurs GPU : ajouter un deuxième GPU aide à réduire fortement les temps de calcul (d’un facteur légèrement inférieur à deux), nettement moins impressionnant en ajoutant deux autres (facteur de deux et demi). Le gain sera probablement plus important avec les prochaines architectures de GPU, Pascal devant utiliser la technologie NVLink au lieu du bus PCI Express (partagé) pour la communication entre cartes.

DIGITS

Algèbre linéaire creuse

CUDA vient également avec la bibliothèque cuSPARSE pour l’algèbre linéaire sur des matrices creuses accélérée sur GPU. Une nouvelle opération vient d’y être ajoutée, nommée GEMVI, utilisée pour la multiplication entre une matrice pleine et un vecteur creux — la sortie étant évidemment un vecteur plein. Ce genre d’opérations est très utile pour l’apprentissage automatique, plus particulièrement dans le cas du traitement des langues. En effet, dans ce cas, un document rédigé dans une langue quelconque (français, anglais, allemand…) peut être représenté comme un comptage des occurrences de mots d’un dictionnaire ; bien évidemment, tous les mots du dictionnaire (même partiel) ne sont pas présents dans le texte, sa représentation vectorielle contient donc un grand nombre de zéros, il est donc creux. Une fois le dictionnaire défini, pour améliorer l’efficacité des traitements, le dictionnaire peut être réduit en taille pour n’en garder qu’un sous-espace vectoriel qui préserve la sémantique des textes : la transformation de la représentation du texte demande justement un produit entre le vecteur creux initial et une matrice de transformation.

C++11 et fonctions anonymes

La version précédente de CUDA a commencé à comprendre C++11, la dernière itération du langage de programmation. Les fonctions anonymes (lambdas) en font partie et servent notamment à écrire du code plus concis. CUDA 7.0 ne les tolérait que dans le code exécuté côté client, pas encore sur le GPU : ce point est corrigé, mais seulement comme fonctionnalité expérimentale. Par exemple, un code comptant les fréquences de quatre lettres dans une chaîne de caractères pourra s’écrire comme ceci :

void xyzw_frequency_thrust_device(int *count, char *text, int n) {
  using namespace thrust;

  *count = count_if(device, text, text+n, [] __device__ (char c) {
    for (const auto x : { 'x','y','z','w' })
      if (c == x) return true;
    return false;
  });
}

La même fonctionnalité permet d’écrire des boucles for pour une exécution en parallèle, avec une syntaxe similaire à OpenMP, par exemple une somme de deux vecteurs (SAXPY pour BLAS) :

void saxpy(float *x, float *y, float a, int N) {
    using namespace thrust;
    auto r = counting_iterator(0);
    for_each(device, r, r+N, [=] __device__ (int i) {
        y[i] = a * x[i] + y[i];
    });
}

Profilage

La dernière nouveauté annoncée concerne le profilage de code, nécessaire pour déterminer les endroits où les efforts d’amélioration de la performance doivent être investis en priorité. CUDA 7.5 améliore les outils NVIDIA Visual Profiler et NSight Eclipse Edition en proposant un profilage au niveau de l’instruction PTX (uniquement sur les GPU Maxwell GM200 et plus récents), pour détermines les lignes précises dans le code qui causent un ralentissement. Précédemment, le profilage ne pouvait se faire qu’au niveau d’un noyau, équivalent d’une fonction pour la programmation sur GPU (temps pris par le noyau à l’exécution, importance relative par rapport à l’exécution complète).

CUDA 6 avait déjà amélioré la situation en affichant une corrélation entre les lignes de code et le nombre d’instructions correspondantes. Cependant, un grand nombre d’instructions n’indique pas forcément que le noyau correspondant prendra beaucoup de temps à l’exécution. Pour remonter jusqu’à la source du problème, ces informations sont certes utiles, mais pas suffisantes, à moins d’avoir une grande expérience. Grâce à CUDA 7.5, le profilage se fait de manière beaucoup plus traditionnelle, avec un échantillonnage de l’exécution du programme, pour trouver les lignes qui prennent le plus de temps.

Sources et crédit des images : New Features in CUDA 7.5, NVIDIA @ ICML 2015: CUDA 7.5, cuDNN 3, & DIGITS 2 Announced.

Nouveau, le pilote NVIDIA libre pour Linux, s’ouvre à CUDA

Vulkan est prévue comme la prochaine évolution d’OpenGL, visant principalement à la rendre plus bas niveau, pour exploiter au mieux les possibilités des processeurs graphiques modernes (voir Vulkan : la nouvelle bibliothèque de hautes performances pour le GPU pour plus de détails). L’une de ses améliorations est SPIR-V, une représentation intermédiaire des shaders, une forme d’assembleur indépendant de la carte graphique utilisé pour représenter tout le code qu’un utilisateur peut y envoyer. Cette représentation intermédiaire sera partagée par OpenCL, prévu pour faciliter les calculs génériques sur GPU (comme la simulation de fluides), à l’instar de CUDA, une technologie propriétaire de NVIDIA, antérieure à OpenCL mais à l’évolution plus rapide.

Dans tout ce beau monde, peu d’annonces du côté du logiciel libre, si ce n’est un pilote pour les puces Intel développé par Valve et qui devrait être libéré. Récemment, Pierre Moreau a annoncé avoir commencé à travailler sur une passe de compilation SPIR-V vers la représentation intermédiaire des GPU NVIDIA NV50, déjà relativement anciens (série GeForce 8000, de 2006-2007). Cette étape est l’une des premières requises pour exécuter du code SPIR-V sur ces GPU.

En comptant les efforts actuels pour développer une passe de compilation LLVM IR vers SPIR-V, grâce à laquelle tout langage que LLVM et ses projets annexes (dont Clang) peuvent compiler pourrait être utilisé pour les GPU. En particulier, le code CUDA pourrait être transformé de la sorte et être exécuté sur Nouveau.

NVIDIA explore une autre piste pour exécuter du code CUDA grâce au pilote libre Nouveau. Leur intérêt pour ce pilote n’est pas neuf, mais il s’est focalisé sur leurs systèmes monopuces (SoC) Tegra, négligeant leurs autres GPU. Tout comme les efforts de Pierre Moreau, rien n’est actuellement visible au grand public en ce qui concerne le code ; cette fois, le développeur de NVIDIA est plus détaillé, en demandant des avis sur des modifications à apporter au code de la gestion de la mémoire du GPU.

Plus techniquement, il explique que CUDA a besoin d’avoir les mêmes adresses virtuelles sur le GPU et sur le CPU, ce que Nouveau ne permet pas encore. De plus, une application CUDA traditionnelle a tendance à allouer une grande quantité de mémoire en avance, pour une utilisation plus tardive ; des morceaux de cet espace sont alors alloués au fur et à mesure des besoins à l’exécution. Nouveau fusionne les opérations d’allocation des adresses virtuelles et de correspondance de mémoire, qui devraient être découplées pour ces utilisations.

Sources : Someone Is Already Working On SPIR-V For The Nouveau Driver, [Nouveau] Will nouveau support for cuda?, [LLVMdev] [RFC] Proposal for Adding SPIRV Target, NVIDIA Has Someone Working On Nouveau CUDA Support