L'optimisation du GPU est une vérité commune. Informatique GPU : mythes et réalité GPU et opérations de mémoire

Calcul GPU avec C++ AMP

Jusqu'à présent, dans la discussion des techniques de programmation parallèle, nous n'avons considéré que les cœurs de processeur. Nous avons acquis des compétences pour paralléliser des programmes sur plusieurs processeurs, synchroniser l'accès aux ressources partagées et utiliser des primitives de synchronisation à grande vitesse sans utiliser de verrous.

Cependant, il existe un autre moyen de paralléliser des programmes - unité de traitement graphique (GPU) qui ont plus de cœurs que même les processeurs hautes performances. Les cœurs GPU sont parfaits pour implémenter des algorithmes de traitement de données parallèles, et un grand nombre d'entre eux compense largement l'inconvénient d'exécuter des programmes sur eux. Dans cet article, nous allons nous familiariser avec l'une des façons d'exécuter des programmes sur le GPU, en utilisant un ensemble d'extensions de langage C ++ appelées C++ AMP.

Les extensions C++ AMP sont basées sur le langage C++, c'est pourquoi cet article présentera des exemples en C++. Cependant, avec une utilisation modérée du mécanisme d'interaction dans. NET, vous pourrez utiliser les algorithmes C++ AMP dans vos programmes .NET. Mais nous en parlerons à la fin de l'article.

Introduction à C++ AMP

En fait, un GPU est le même processeur que n'importe quel autre, mais avec un jeu d'instructions spécial, un grand nombre de cœurs et son propre protocole d'accès mémoire. Cependant, il existe de grandes différences entre les processeurs graphiques modernes et les processeurs conventionnels, et les comprendre est la clé pour créer des programmes qui utilisent efficacement la puissance de traitement du GPU.

    Les GPU modernes ont un très petit jeu d'instructions. Cela implique certaines limitations : le manque de capacité à appeler des fonctions, l'ensemble limité de types de données pris en charge, le manque de fonctions de bibliothèque, etc. Certaines opérations, telles que les sauts conditionnels, peuvent coûter beaucoup plus cher que des opérations similaires effectuées sur des processeurs conventionnels. Il est évident que déplacer de grandes quantités de code du processeur vers le GPU dans de telles conditions nécessite des efforts importants.

    Le nombre de cœurs dans le GPU moyen est nettement supérieur à celui du processeur conventionnel moyen. Cependant, certaines tâches sont trop petites ou ne se laissent pas décomposer en un nombre de parties suffisamment important pour bénéficier de l'utilisation du GPU.

    La prise en charge de la synchronisation entre les cœurs GPU effectuant la même tâche est très rare et totalement absente entre les cœurs GPU effectuant des tâches différentes. Cette circonstance nécessite une synchronisation du GPU avec un processeur conventionnel.

La question se pose immédiatement, quelles tâches sont adaptées à la résolution d'un processeur graphique? Gardez à l'esprit que tous les algorithmes ne sont pas adaptés à une exécution sur un GPU. Par exemple, les GPU n'ont pas accès aux périphériques d'E/S, vous ne pouvez donc pas améliorer les performances d'un programme qui récupère les flux RSS sur Internet en utilisant le GPU. Cependant, de nombreux algorithmes de calcul peuvent être transférés vers le GPU et massivement parallélisés. Voici quelques exemples de tels algorithmes (cette liste est loin d'être complète) :

    augmenter et diminuer la netteté des images et d'autres transformations ;

    Transformée de Fourier Rapide;

    transposition et multiplication matricielle ;

    trier les numéros;

    inversion de hachage "dans le front".

Le blog Microsoft Native Concurrency est une excellente source d'exemples supplémentaires, qui fournit des extraits de code et des explications pour divers algorithmes implémentés dans C++ AMP.

C ++ AMP est un framework inclus avec Visual Studio 2012 qui offre aux développeurs C ++ un moyen simple d'effectuer des calculs GPU avec uniquement le pilote DirectX 11. Microsoft a publié C ++ AMP en tant que spécification ouverte que tout fournisseur de compilateur peut implémenter .

Le framework C++ AMP vous permet d'exécuter du code dans accélérateurs graphiques qui sont des appareils informatiques. Avec le pilote DirectX 11, le framework C++ AMP détecte dynamiquement tous les accélérateurs. C++ AMP comprend également un accélérateur logiciel et un émulateur de processeur conventionnel, WARP, qui sert de solution de repli sur les systèmes sans GPU ou avec GPU mais sans pilote DirectX 11, et utilise plusieurs cœurs et instructions SIMD.

Commençons maintenant à explorer un algorithme qui peut être facilement parallélisé pour s'exécuter sur un GPU. L'implémentation ci-dessous prend deux vecteurs de même longueur et calcule le résultat ponctuel. Difficile d'imaginer quelque chose de plus simple :

Void VectorAddExpPointwise (float * first, float * second, float * result, int length) (for (int i = 0; i< length; ++i) { result[i] = first[i] + exp(second[i]); } }

Pour paralléliser cet algorithme sur un processeur classique, il est nécessaire de diviser la plage d'itérations en plusieurs sous-plages et de lancer un thread d'exécution pour chacune d'entre elles. Nous avons consacré beaucoup de temps dans les articles précédents à exactement cette façon de paralléliser notre premier exemple de recherche de nombres premiers - nous avons vu comment cela peut être fait en créant des threads manuellement, en passant des tâches à un pool de threads et en utilisant Parallel.For et PLINQ pour paralléliser automatiquement. Rappelez-vous également que lors de la parallélisation d'algorithmes similaires sur un processeur ordinaire, nous avons pris soin de ne pas diviser la tâche en tâches trop petites.

Pour le GPU, ces avertissements sont inutiles. Les GPU ont de nombreux cœurs qui exécutent les threads très rapidement, et le coût du changement de contexte est nettement inférieur à celui des processeurs conventionnels. Voici un extrait essayant d'utiliser la fonction parallel_for_each du framework C++ AMP :

#comprendre #comprendre utilisation de la concurrence d'espace de noms ; void VectorAddExpPointwise (float * first, float * second, float * result, int length) (array_view avFirst (longueur, premier); tableau_vue avSecond (longueur, seconde); tableau_vue avResult (longueur, résultat); avResult.discard_data (); parallel_for_each (avResult.extent, [=] (index<1>i) restreindre (amp) (avResult [i] = avFirst [i] + fast_math :: exp (avSecond [i]);)); avResult.synchronize (); )

Examinons maintenant chaque partie du code séparément. Notez tout de suite que la forme générale de la boucle principale a été conservée, mais la boucle for originale a été remplacée par un appel à parallel_for_each. En fait, le principe de convertir une boucle en appel de fonction ou de méthode n'est pas nouveau pour nous - cette technique a déjà été démontrée à l'aide des méthodes Parallel.For() et Parallel.ForEach() de la bibliothèque TPL.

De plus, les données d'entrée (paramètres first, second et result) sont encapsulées dans des instances tableau_vue... La classe array_view est utilisée pour envelopper les données transmises au GPU (accélérateur). Son paramètre de modèle définit le type de données et leur dimension. Afin d'exécuter des instructions sur le GPU qui accèdent aux données traitées à l'origine sur un CPU conventionnel, quelqu'un ou quelque chose doit s'occuper de copier les données sur le GPU, car la plupart des cartes graphiques modernes sont des appareils séparés avec leur propre mémoire. Les instances Array_view le font en garantissant que les données sont copiées à la demande et uniquement lorsque cela est vraiment nécessaire.

Lorsque le GPU a terminé le travail, les données sont copiées. En instanciant array_view avec un argument const, nous nous assurons que le premier et le second sont copiés dans la mémoire GPU, mais pas copiés. De même, appeler jeter_données (), nous excluons la copie du résultat de la mémoire d'un processeur conventionnel vers la mémoire de l'accélérateur, mais ces données seront copiées dans le sens inverse.

La fonction parallel_for_each prend un objet d'étendue qui définit la forme des données à traiter et une fonction à appliquer à chaque élément de l'objet d'étendue. Dans l'exemple ci-dessus, nous avons utilisé une fonction lambda, dont le support est apparu dans la norme ISO C++ 2011 (C++ 11). Le mot-clé restrict (amp) demande au compilateur de vérifier que le corps de la fonction peut être exécuté sur le GPU et désactive une grande partie de la syntaxe C++ qui ne peut pas être compilée dans les instructions GPU.

Paramètre de fonction lambda, index<1>objet, représente un indice unidimensionnel. Il doit correspondre à l'objet d'étendue utilisé - si nous devions déclarer l'objet d'étendue à deux dimensions (par exemple, en définissant la forme des données d'origine comme une matrice à deux dimensions), l'index devrait également être de deux -dimensionnel. Un exemple d'une telle situation est donné ci-dessous.

Enfin, l'appel de méthode synchroniser ()à la fin de la méthode VectorAddExpPointwise, garantit que les résultats de calcul de array_view avResult produits par le GPU sont copiés dans le tableau de résultats.

Ceci conclut notre première rencontre avec le monde de C ++ AMP, et nous sommes maintenant prêts pour des recherches plus détaillées, ainsi que des exemples plus intéressants démontrant les avantages de l'utilisation du calcul parallèle sur un GPU. L'ajout de vecteurs n'est pas le meilleur algorithme ni le meilleur candidat pour démontrer l'utilisation du GPU en raison de la surcharge importante de la copie des données. Dans la sous-section suivante, deux autres exemples intéressants seront présentés.

Multiplication matricielle

Le premier exemple "réel" que nous examinerons est la multiplication matricielle. Pour l'implémentation, nous prendrons un simple algorithme de multiplication matricielle cubique, et non l'algorithme de Strassen, qui a un temps d'exécution proche de cubique ~ O (n 2.807). Pour deux matrices, une matrice m x w A et une matrice w x n B, le programme suivant les multipliera et renverra le résultat, une matrice m x n C :

Void MatrixMultiply (int * A, int m, int w, int * B, int n, int * C) (pour (int i = 0; i< m; ++i) { for (int j = 0; j < n; ++j) { int sum = 0; for (int k = 0; k < w; ++k) { sum += A * B; } C = sum; } } }

Il existe plusieurs façons de paralléliser cette implémentation, et si vous souhaitez paralléliser ce code pour l'exécuter sur un processeur normal, le bon choix serait de paralléliser la boucle externe. Cependant, le GPU a un nombre de cœurs suffisamment important, et en parallélisant uniquement la boucle externe, nous ne pouvons pas créer suffisamment de tâches pour charger tous les cœurs de travail. Par conséquent, il est logique de paralléliser les deux boucles externes tout en laissant la boucle interne intacte :

Void MatrixMultiply (int * A, int m, int w, int * B, int n, int * C) (array_view avA (m, w, A); tableau_vue avB (w, n, B); tableau_vue avC (m, n, C); avC.discard_data (); parallel_for_each (avC.extent, [=] (index<2>idx) restrict (amp) (int sum = 0; for (int k = 0; k< w; ++k) { sum + = avA(idx*w, k) * avB(k*w, idx); } avC = sum; }); }

Cette implémentation ressemble toujours beaucoup à l'exemple de multiplication matricielle séquentielle et d'addition vectorielle ci-dessus, à l'exception de l'index, qui est désormais bidimensionnel et est disponible dans la boucle interne à l'aide de l'opérateur. Combien plus rapide cette version de l'alternative séquentielle s'exécute-t-elle sur un processeur ordinaire ? En multipliant deux matrices 1024 x 1024 (entiers), la version séquentielle sur le processeur ordinaire donne en moyenne 7350 millisecondes, tandis que la version GPU - tenez-vous bien - 50 millisecondes, 147 fois plus rapide !

Simulation de mouvement de particules

Les exemples de résolution de problèmes sur le GPU présentés ci-dessus ont une implémentation très simple de la boucle interne. Il est clair que ce ne sera pas toujours le cas. Le blog Native Concurrency, lié ci-dessus, montre un exemple de modélisation des interactions gravitationnelles entre les particules. La simulation comprend un nombre infini d'étapes ; à chaque étape, de nouvelles valeurs des éléments du vecteur d'accélération pour chaque particule sont calculées puis leurs nouvelles coordonnées sont déterminées. Ici, un vecteur de particules est soumis à la parallélisation - avec un nombre suffisamment grand de particules (de plusieurs milliers et plus), vous pouvez créer un nombre suffisamment grand de tâches pour charger tous les cœurs GPU de travail.

La base de l'algorithme est la mise en œuvre de la détermination du résultat des interactions entre deux particules, comme indiqué ci-dessous, qui peut être facilement transféré au GPU :

// ici float4 sont des vecteurs avec quatre éléments // représentant des particules participant aux opérations void bodybody_interaction (float4 & accélération, const float4 p1, const float4 p2) restrict (amp) (float4 dist = p2 - p1; // w pas ici utilisé float absDist = dist.x * dist.x + dist.y * dist.y + dist.z * dist.z ; float invDist = 1.0f / sqrt (absDist); float invDistCube = invDist * invDist * invDist ; accélération + = dist * PARTICLE_MASS * invDistCube ;)

Les données initiales à chaque étape de la modélisation sont un tableau avec les coordonnées et les vitesses des particules, et à la suite des calculs, un nouveau tableau avec les coordonnées et les vitesses des particules est créé :

Particule de structure (position float4, vélocité ; // constructeur, constructeur de copie et // opérateur = avec restriction (amp) omis pour économiser de l'espace); void simulation_step (tableau & précédent, tableau & suivant, corps entiers) (étendue<1>poste (corps); parallel_for_each (ext, [&] (index<1>idx) restrict (amp) (particule p = précédent; float4 accélération (0, 0, 0, 0); for (int body = 0; body< bodies; ++body) { bodybody_interaction (acceleration, p.position, previous.position); } p.velocity + = acceleration*DELTA_TIME; p.position + = p.velocity*DELTA_TIME; next = p; }); }

A l'aide de l'interface graphique appropriée, la modélisation peut être très intéressante. Un exemple complet fourni par l'équipe de développement C++ AMP est disponible sur le blog Native Concurrency. Sur mon système avec un processeur Intel Core i7 et une carte graphique Geforce GT 740M, 10 000 simulations de mouvement de particules sont effectuées à environ 2,5 images par seconde (pas par seconde) en utilisant une version séquentielle fonctionnant sur un processeur ordinaire et 160 images par seconde en utilisant un optimisé la version s'exécutant sur le GPU est une énorme augmentation des performances.

Avant de conclure cette section, il existe une autre caractéristique importante du framework C ++ AMP qui peut encore améliorer les performances du code exécuté sur le GPU. Prise en charge des GPU cache de données programmable(souvent appelé la memoire partagée). Les valeurs stockées dans ce cache sont partagées par tous les threads dans une seule tuile. Avec la mosaïque de mémoire, les programmes C ++ AMP peuvent lire les données de la mémoire de la carte graphique dans la mémoire partagée en mosaïque, puis y accéder à partir de plusieurs threads d'exécution sans extraire ces données de la mémoire de la carte graphique. La mémoire partagée Mosaic accède environ 10 fois plus rapidement que la mémoire de la carte graphique. En d'autres termes, vous avez une raison de continuer à lire.

Pour s'assurer que la version en mosaïque de la boucle parallèle est exécutée, la méthode parallel_for_each est transmise domaine tiled_extent, qui divise l'étendue multidimensionnelle en tuiles multidimensionnelles, et le lambda tiled_index, qui spécifie les ID de thread global et local dans les tuiles. Par exemple, une matrice 16x16 peut être divisée en tuiles 2x2 (comme indiqué dans l'image ci-dessous) puis transmise à la fonction parallel_for_each :

Le degré<2>matrice (16,16); tile_extent<2,2>TiledMatrix = matrice.tile<2,2>(); parallel_for_each (tiledMatrix, [=] (tiled_index<2,2>idx) restreindre (amp) (// ...));

Chacun des quatre threads d'exécution appartenant à une même mosaïque peut partager les données stockées dans le bloc.

Lors de l'exécution d'opérations avec des matrices, dans le cœur du GPU, au lieu de l'index d'index standard<2>comme dans les exemples ci-dessus, vous pouvez utiliser idx.global... L'utilisation intelligente de la mémoire tuilée locale et des index locaux peut fournir des gains de performances significatifs. Les variables locales peuvent être déclarées avec le spécificateur tile_static pour déclarer la mémoire en mosaïque partagée par tous les threads d'exécution dans une seule mosaïque.

En pratique, la technique consistant à déclarer la mémoire partagée et à initialiser ses blocs individuels dans différents threads d'exécution est souvent utilisée :

Parallel_for_each (tiledMatrix, [=] (tiled_index<2,2>idx) restrict (amp) (// 32 octets sont partagés par tous les threads dans un bloc tile_static int local ; // attribue une valeur à un élément pour ce thread local = 42 ;));

Bien entendu, tout bénéfice de l'utilisation de la mémoire partagée ne peut être obtenu que si l'accès à cette mémoire est synchronisé ; c'est-à-dire que les threads ne doivent pas accéder à la mémoire tant qu'elle n'a pas été initialisée par l'un d'entre eux. La synchronisation des fils dans une mosaïque se fait à l'aide d'objets tuile_barrière(similaire à la classe Barrier de la bibliothèque TPL) - ils ne peuvent continuer l'exécution qu'après avoir appelé tile_barrier.Wait(), qui ne retournera que lorsque tous les threads appelleront tile_barrier.Wait. Par exemple:

Parallel_for_each (tiledMatrix, (tiled_index<2,2>idx) restrict (amp) (// 32 octets sont partagés par tous les threads d'un bloc tile_static int local ; // attribue une valeur à un élément pour ce thread d'exécution local = 42 ; // idx.barrier est une instance de tile_barrier idx .barrier.wait (); // Maintenant ce thread peut accéder au tableau "local" // en utilisant les indices des autres threads !));

Il est maintenant temps de traduire ces connaissances en un exemple concret. Revenons à l'implémentation de la multiplication matricielle, effectuée sans utiliser de pavage mémoire, et ajoutons-y l'optimisation décrite. Supposons que la taille de la matrice soit un multiple de 256 - cela nous permettra de travailler avec des blocs 16 x 16. Utilisation du cache CPU).

L'essence de cette technique est la suivante. Pour trouver C i, j (l'élément de la ligne i et de la colonne j de la matrice de résultat), vous devez calculer le produit scalaire entre A i, * (i-ième ligne de la première matrice) et B *, j ( j-ième colonne dans la deuxième matrice ). Cependant, cela équivaut à calculer les produits scalaires partiels d'une ligne et d'une colonne, puis à additionner les résultats. Nous pouvons utiliser cette circonstance pour transformer l'algorithme de multiplication matricielle en une version en mosaïque :

Void MatrixMultiply (int * A, int m, int w, int * B, int n, int * C) (array_view avA (m, w, A); tableau_vue avB (w, n, B); tableau_vue avC (m, n, C); avC.discard_data (); parallel_for_each (avC.extent.tile<16,16>(), [=] (tiled_index<16,16>idx) restrict (amp) (int sum = 0 ; int localRow = idx.local, localCol = idx.local ; for (int k = 0; k

L'essence de l'optimisation décrite est que chaque thread de la mosaïque (256 threads sont créés pour un bloc 16 x 16) initialise son élément en 16 x 16 copies locales de fragments des matrices d'origine A et B. Chaque thread de la mosaïque a besoin une seule ligne et une colonne de ces blocs, mais tous les threads ensemble accéderont à chaque ligne et à chaque colonne 16 fois. Cette approche réduit considérablement le nombre d'appels à la mémoire principale.

Pour calculer l'élément (i, j) dans la matrice de résultat, l'algorithme nécessite la ième ligne complète de la première matrice et la j-ième colonne de la deuxième matrice. Lorsque les flux de mosaïque 16x16 sont tracés et k = 0, les zones ombrées dans les première et deuxième matrices seront lues dans la mémoire partagée. Le fil d'exécution calculant l'élément (i, j) dans la matrice de résultat calculera le produit scalaire partiel des k premiers éléments à partir de la i-ème ligne et de la j-ème colonne des matrices d'origine.

Dans cet exemple, l'utilisation du carrelage permet d'énormes gains de performances. La version en mosaïque de la multiplication matricielle est beaucoup plus rapide que la version simple et prend environ 17 millisecondes (pour les mêmes matrices d'origine 1024 x 1024), ce qui est 430 plus rapide que la version fonctionnant sur un processeur ordinaire !

Avant de terminer notre discussion sur le framework C++ AMP, nous aimerions mentionner les outils (dans Visual Studio) dont les développeurs disposent. Visual Studio 2012 propose un débogueur d'unité de traitement graphique (GPU) qui permet de définir des points d'arrêt, d'examiner la pile d'appels, de lire et de modifier les valeurs des variables locales (certains accélérateurs prennent directement en charge le débogage GPU ; pour d'autres, Visual Studio utilise un logiciel simulateur) et un profileur qui permet d'évaluer les avantages de la parallélisation des opérations à l'aide du GPU vers une application. Pour plus d'informations sur les fonctionnalités de débogage dans Visual Studio, consultez l'article Procédure pas à pas. Débogage d'une application C++ AMP » sur MSDN.

Alternatives de calcul GPU dans .NET

Jusqu'à présent, cet article a montré des exemples en C ++ uniquement, cependant, il existe plusieurs façons d'exploiter la puissance du GPU dans les applications gérées. Une façon consiste à utiliser des outils d'interopérabilité pour déplacer le travail des cœurs GPU vers des composants C ++ de bas niveau. Cette solution est idéale pour ceux qui souhaitent utiliser le framework C ++ AMP ou qui ont la possibilité d'utiliser des composants C ++ AMP prêts à l'emploi dans des applications gérées.

Une autre façon consiste à utiliser une bibliothèque qui fonctionne directement avec le GPU à partir de code managé. Il existe actuellement plusieurs bibliothèques de ce type. Par exemple GPU.NET et CUDAfy.NET (les deux sont des offres commerciales). Vous trouverez ci-dessous un exemple du référentiel GPU.NET GitHub illustrant la mise en œuvre du produit scalaire de deux vecteurs :

Public static void MultiplyAddGpu (double a, double b, double c) (int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; int TotalThreads = BlockDimension.X * GridDimension.X; for (int ElementIdx = ThreadId; ElementIdx

Je suis d'avis qu'il est beaucoup plus facile et efficace de maîtriser l'extension du langage (basé sur C++ AMP) que d'essayer d'organiser les interactions au niveau de la bibliothèque ou d'apporter des modifications significatives au langage IL.

Ainsi, après avoir examiné les possibilités de programmation parallèle en .NET et l'utilisation du GPU, il ne fait probablement aucun doute que l'organisation du calcul parallèle est un moyen important d'améliorer les performances. Dans de nombreux serveurs et postes de travail à travers le monde, la puissance de traitement inestimable des processeurs conventionnels et GPU n'est pas utilisée car les applications ne les utilisent tout simplement pas.

La bibliothèque parallèle de tâches nous offre une opportunité unique d'inclure tous les cœurs de processeur disponibles dans le travail, même si elle devra résoudre des problèmes intéressants de synchronisation, de surfragmentation des tâches et de répartition inégale du travail entre les threads d'exécution.

Le framework C ++ AMP et d'autres bibliothèques de calcul parallèle GPU polyvalentes peuvent être utilisés avec succès pour paralléliser les calculs sur des centaines de cœurs GPU. Enfin, il existe, jusqu'alors inexplorée, l'opportunité d'obtenir des gains de performances grâce à l'utilisation des technologies cloud de l'informatique distribuée, qui sont récemment devenues l'une des principales directions de développement des technologies de l'information.

Un jour, j'ai eu la chance de parler du marché de l'informatique avec le directeur technique de l'une des nombreuses entreprises qui vendent des ordinateurs portables. Ce "spécialiste" a essayé de mousser à la bouche pour expliquer de quel type de configuration d'ordinateur portable j'ai besoin. Le message principal de son monologue était que le temps des unités centrales de traitement (CPU) est révolu, et maintenant toutes les applications utilisent activement des calculs sur une unité de traitement graphique (GPU), et donc les performances d'un ordinateur portable dépendent entièrement du GPU, et vous ne pouvez pas faire attention. Réalisant qu'il est absolument inutile de discuter et d'essayer de raisonner avec ce directeur technique, je n'ai pas perdu de temps et j'ai acheté l'ordinateur portable dont j'avais besoin dans un autre pavillon. Cependant, le fait même d'une telle incompétence flagrante du vendeur m'a frappé. Ce serait compréhensible s'il essayait de me tromper en tant qu'acheteur. Pas du tout. Il croyait vraiment en ce qu'il disait. Oui, apparemment, les spécialistes du marketing chez NVIDIA et AMD ne mangent pas leur pain en vain, et ils ont quand même réussi à inculquer à certains utilisateurs l'idée du rôle dominant du GPU dans un ordinateur moderne.

Il ne fait aucun doute que l'informatique graphique (GPU) est de plus en plus populaire aujourd'hui. Cependant, cela ne diminue pas le rôle du processeur central. De plus, si nous parlons de l'écrasante majorité des applications utilisateur, alors aujourd'hui, leurs performances dépendent entièrement des performances du processeur. C'est-à-dire que l'écrasante majorité des applications utilisateur n'utilise pas le GPU computing.

En général, le calcul GPU est principalement effectué sur des systèmes HPC spécialisés pour le calcul scientifique. Mais les applications personnalisées qui utilisent le GPU computing peuvent être comptées sur une seule main. Il convient de noter tout de suite que le terme "calcul GPU" dans ce cas n'est pas tout à fait correct et peut être trompeur. Le fait est que si une application utilise le GPU computing, cela ne signifie pas que le processeur central est inactif. Calculer sur un GPU n'implique pas de déplacer la charge du CPU vers le GPU. En règle générale, le processeur central reste chargé et l'utilisation du processeur graphique, avec le processeur central, peut améliorer les performances, c'est-à-dire réduire le temps d'exécution de la tâche. De plus, le GPU lui-même agit ici comme une sorte de coprocesseur pour le CPU, mais ne le remplace en aucun cas complètement.

Pour comprendre pourquoi le calcul sur GPU n'est pas une sorte de panacée et pourquoi il est faux de prétendre que leurs capacités de calcul dépassent celles d'un CPU, il faut comprendre la différence entre un processeur central et un processeur graphique.

Différences dans les architectures GPU et CPU

Les cœurs de processeur sont conçus pour exécuter un seul flux d'instructions séquentiel à des performances maximales, tandis que les GPU sont conçus pour exécuter rapidement un très grand nombre de flux d'instructions simultanés. C'est la différence fondamentale entre les GPU et les centraux. Un processeur est un processeur à usage général ou à usage général optimisé pour des performances élevées à partir d'un seul flux d'instructions qui traite à la fois les entiers et les nombres à virgule flottante. Dans ce cas, l'accès à la mémoire avec des données et des instructions se fait principalement de manière aléatoire.

Pour améliorer les performances du processeur, ils sont conçus pour exécuter autant d'instructions en parallèle que possible. Par exemple, pour cela, les coeurs de processeurs utilisent un bloc d'exécution désordonnée de commandes, qui permet de réordonner des instructions désordonnées de leur arrivée, ce qui permet d'élever le niveau de parallélisme de la mise en œuvre des instructions au niveau d'un fil. Néanmoins, cela ne permet toujours pas l'exécution parallèle d'un grand nombre d'instructions, et le surcoût de parallélisation des instructions au sein du cœur du processeur s'avère très important. C'est pourquoi les processeurs généralistes ne disposent pas d'un très grand nombre d'unités d'exécution.

Le processeur graphique est conçu d'une manière fondamentalement différente. Il a été conçu à l'origine pour exécuter un grand nombre de flux d'instructions parallèles. De plus, ces flux d'instructions sont initialement parallélisés et il n'y a tout simplement pas de surcharge pour la parallélisation des instructions dans le GPU. Le GPU est conçu pour rendre l'image. Pour faire simple, il accepte un groupe de polygones en entrée, effectue toutes les opérations nécessaires et sort des pixels en sortie. Le traitement des polygones et des pixels est indépendant, ils peuvent être traités en parallèle, séparément les uns des autres. Par conséquent, en raison de l'organisation initialement parallèle du travail dans le GPU, un grand nombre d'unités d'exécution sont utilisées, qui sont faciles à charger, contrairement au flux séquentiel d'instructions pour le CPU.

Les graphiques et les unités centrales de traitement diffèrent également dans les principes d'accès à la mémoire. Dans le GPU, l'accès à la mémoire est facilement prévisible : si un texel de texture est lu à partir de la mémoire, après un certain temps, le temps viendra également pour les texels voisins. Lors de l'écriture, la même chose se produit : si un pixel est écrit dans le framebuffer, alors après quelques cycles d'horloge, le pixel situé à côté sera écrit. Par conséquent, un GPU, contrairement à un CPU, n'a tout simplement pas besoin d'une grande mémoire cache et les textures ne nécessitent que quelques kilo-octets. Le principe de travailler avec la mémoire pour le GPU et le CPU est également différent. Ainsi, tous les GPU modernes ont plusieurs contrôleurs de mémoire, et la mémoire graphique elle-même est plus rapide, donc les GPU ont beaucoup plus O Bande passante mémoire plus élevée par rapport aux processeurs à usage général, ce qui est également très important pour les calculs parallèles fonctionnant avec d'énormes flux de données.

Dans les processeurs à usage général O La majeure partie de la zone de puce est occupée par divers tampons de commande et de données, des blocs de décodage, des blocs de prédiction de branchement matériel, des blocs de réorganisation d'instructions et une mémoire cache des premier, deuxième et troisième niveaux. Tous ces blocs matériels sont nécessaires pour accélérer l'exécution de quelques flux d'instructions en les parallélisant au niveau du cœur du processeur.

Les unités d'exécution elles-mêmes occupent relativement peu de place dans le processeur universel.

Dans un processeur graphique, au contraire, la zone principale est occupée par précisément de nombreuses unités d'exécution, ce qui lui permet de traiter simultanément plusieurs milliers de flux d'instructions.

On peut dire que, contrairement aux CPU modernes, les GPU sont conçus pour le calcul parallèle avec beaucoup d'opérations arithmétiques.

Il est possible d'utiliser la puissance de calcul des processeurs graphiques pour des tâches non graphiques, mais uniquement si le problème à résoudre permet de paralléliser les algorithmes en centaines d'unités d'exécution disponibles dans le GPU. En particulier, les calculs GPU montrent d'excellents résultats lorsque la même séquence d'opérations mathématiques est appliquée à une grande quantité de données. Dans ce cas, les meilleurs résultats sont obtenus si le rapport du nombre d'instructions arithmétiques sur le nombre d'accès mémoire est suffisamment grand. Cette opération demande moins de contrôle d'exécution et n'a pas besoin d'utiliser une grande mémoire cache.

Il existe de nombreux exemples de calculs scientifiques où l'avantage d'un GPU par rapport à un CPU en termes d'efficacité de calcul est indéniable. Ainsi, de nombreuses applications scientifiques sur la modélisation moléculaire, la dynamique des gaz, la dynamique des fluides et autres sont parfaitement adaptées aux calculs sur GPU.

Ainsi, si l'algorithme de résolution d'un problème peut être parallélisé en des milliers de threads distincts, l'efficacité de la résolution d'un tel problème à l'aide d'un GPU peut être supérieure à celle de sa résolution en utilisant uniquement un processeur à usage général. Cependant, il n'est pas si facile de prendre et de transférer la solution d'un problème du CPU au GPU, ne serait-ce que simplement parce que le CPU et le GPU utilisent des commandes différentes. Autrement dit, lorsqu'un programme est écrit pour une solution sur un processeur, le jeu d'instructions x86 (ou un jeu d'instructions compatible avec une architecture de processeur spécifique) est utilisé, mais des jeux d'instructions complètement différents sont utilisés pour le GPU, qui prennent à nouveau compte de son architecture et de ses capacités. Lors du développement de jeux 3D modernes, les API DirectX et OpenGL sont utilisées, ce qui permet aux programmeurs de travailler avec des shaders et des textures. Cependant, l'utilisation des API DirectX et OrenGL pour le calcul sans GPU n'est pas la meilleure option.

Application NVIDIA CUDA et AMD

C'est pourquoi, lorsque les premières tentatives d'implémentation de l'informatique non graphique sur le GPU (General Purpose GPU, GPGPU) ont commencé, le compilateur BrookGPU est né. Avant sa création, les développeurs devaient accéder aux ressources de la carte vidéo via les API graphiques OpenGL ou Direct3D, ce qui compliquait considérablement le processus de programmation, car cela nécessitait des connaissances spécifiques - ils devaient apprendre les principes du travail avec des objets 3D (shaders, textures , etc.). C'était la raison de l'utilisation très limitée du GPGPU dans les produits logiciels. BrookGPU est devenu une sorte de "traducteur". Ces extensions de streaming au langage C cachaient l'API 3D aux programmeurs, et lors de son utilisation, le besoin de connaissances en programmation 3D a pratiquement disparu. La puissance de calcul des cartes vidéo est devenue disponible pour les programmeurs sous la forme d'un coprocesseur supplémentaire pour les calculs parallèles. Le compilateur BrookGPU a traité le code C et le fichier d'extensions pour créer un code lié à une bibliothèque DirectX ou OpenGL.

Grâce en grande partie à BrookGPU, NVIDIA et ATI (maintenant AMD) ont tourné leur attention vers la technologie naissante de l'informatique à usage général sur GPU et ont commencé à développer leurs propres implémentations qui offrent un accès direct et plus transparent aux blocs de calcul des accélérateurs 3D.

En conséquence, NVIDIA a développé l'architecture de calcul parallèle CUDA (Compute Unified Device Architecture). L'architecture CUDA permet le calcul non graphique sur les GPU NVIDIA.

La version bêta publique du SDK CUDA est sortie en février 2007. L'API CUDA est basée sur un dialecte C simplifié. L'architecture CUDA SDK fournit aux programmeurs la mise en œuvre d'algorithmes pouvant être exécutés sur les GPU NVIDIA et l'inclusion de fonctions spéciales dans le texte d'un programme C. Pour traduire avec succès le code dans cette langue, le SDK CUDA inclut le propre sycompiler de ligne de commande de NVIDIA nvcc.

CUDA est un logiciel multiplateforme pour les systèmes d'exploitation tels que Linux, Mac OS X et Windows.

AMD (ATI) a également développé sa propre version de la technologie GPGPU, anciennement appelée ATI Stream et maintenant AMD Accelerated Parallel Processing (APP). AMD APP est basé sur la norme industrielle ouverte OpenCL (Open Computing Language). La norme OpenCL fournit un parallélisme au niveau des instructions et des données et est une implémentation de la technique GPGPU. Il s'agit d'un standard complètement ouvert et libre de droits pour son utilisation. Notez que AMD APP et NVIDIA CUDA ne sont pas compatibles entre eux, cependant, la dernière version de NVIDIA CUDA prend également en charge OpenCL.

Test du GPGPU dans les convertisseurs vidéo

Ainsi, nous avons découvert que la technologie CUDA est utilisée pour implémenter GPGPU sur les GPU NVIDIA et que l'API APP est utilisée sur les GPU AMD. Comme déjà noté, l'utilisation de calculs non graphiques sur un GPU n'est conseillée que si le problème à résoudre peut être parallélisé en plusieurs threads. Cependant, la plupart des applications personnalisées ne répondent pas à ce critère. Cependant, il y a certaines exceptions. Par exemple, la plupart des convertisseurs vidéo modernes prennent en charge la possibilité d'utiliser le calcul sur les GPU NVIDIA et AMD.

Pour découvrir avec quelle efficacité le GPU computing est utilisé dans les convertisseurs vidéo personnalisés, nous avons sélectionné trois solutions populaires : Xilisoft Video Converter Ultimate 7.7.2, Wondershare Video Converter Ultimate 6.0.3.2 et Movavi Video Converter 10.2.1. Ces convertisseurs prennent en charge la possibilité d'utiliser les GPU NVIDIA et AMD, et vous pouvez désactiver cette fonctionnalité dans les paramètres du convertisseur vidéo, ce qui vous permet d'évaluer l'efficacité du GPU.

Nous avons utilisé trois vidéos différentes pour la conversion vidéo.

La première vidéo durait 3 minutes 35 secondes et faisait 1,05 Go. Il a été enregistré dans le format de stockage de données (conteneur) mkv et avait les caractéristiques suivantes :

  • vidéo:
    • format - Vidéo MPEG4 (H264),
    • résolution - 1920 * um * 1080,
    • mode débit - Variable,
    • débit vidéo moyen - 42,1 Mbps,
    • débit vidéo maximal - 59,1 Mbps,
    • fréquence d'images - 25 ips;
  • l'audio:
    • format - MPEG-1 Audio,
    • débit audio - 128 Kbps,
    • nombre de canaux - 2,

La deuxième vidéo durait 4 minutes 25 secondes et mesurait 1,98 Go. Il a été enregistré au format de stockage de données MPG (conteneur) et avait les caractéristiques suivantes :

  • vidéo:
    • format - MPEG-PS (vidéo MPEG2),
    • résolution - 1920 * um * 1080,
    • mode débit binaire - Variable.
    • débit vidéo moyen - 62,5 Mbps,
    • débit vidéo maximal - 100 Mbps,
    • fréquence d'images - 25 ips;
  • l'audio:
    • format - MPEG-1 Audio,
    • débit audio - 384 Kbps,
    • nombre de canaux - 2,

La troisième vidéo durait 3 minutes 47 secondes et 197 Mo. Il a été enregistré dans le format de stockage de données (conteneur) MOV et avait les caractéristiques suivantes :

  • vidéo:
    • format - Vidéo MPEG4 (H264),
    • résolution - 1920 * um * 1080,
    • mode débit - Variable,
    • débit vidéo - 7024 Kbps,
    • fréquence d'images - 25 ips;
  • l'audio:
    • format - AAC,
    • débit audio - 256 Kbps,
    • nombre de canaux - 2,
    • fréquence d'échantillonnage - 48 kHz.

Les trois vidéos de test ont été converties à l'aide de convertisseurs vidéo au format de stockage de données MP4 (codec H.264) pour une visualisation sur un iPad 2. La résolution du fichier vidéo de sortie était de 1280 * um * 720.

Notez que nous n'avons pas utilisé exactement les mêmes paramètres de conversion dans les trois convertisseurs. C'est pourquoi il est incorrect de comparer l'efficacité des convertisseurs vidéo en termes de temps de conversion. Ainsi, dans Xilisoft Video Converter Ultimate 7.7.2, le préréglage iPad 2 - H.264 HD Video a été utilisé pour la conversion. Ce préréglage utilise les paramètres d'encodage suivants :

  • codec - MPEG4 (H.264);
  • résolution - 1280 * um * 720;
  • fréquence d'images - 29,97 ips;
  • débit vidéo - 5210 Kbps;
  • codec audio - AAC;
  • débit binaire audio - 128 Kbps;
  • nombre de canaux - 2;
  • fréquence d'échantillonnage - 48 kHz.

Wondershare Video Converter Ultimate 6.0.3.2 a utilisé le préréglage iPad 2 avec les paramètres supplémentaires suivants :

  • codec - MPEG4 (H.264);
  • résolution - 1280 * um * 720;
  • fréquence d'images - 30 ips;
  • débit vidéo - 5000 Kbps;
  • codec audio - AAC;
  • débit binaire audio - 128 Kbps;
  • nombre de canaux - 2;
  • fréquence d'échantillonnage - 48 kHz.

Movavi Video Converter 10.2.1 a utilisé le préréglage iPad (1280 * um * 720, H.264) (* .mp4) avec les paramètres supplémentaires suivants :

  • format vidéo - H.264 ;
  • résolution - 1280 * um * 720;
  • fréquence d'images - 30 ips;
  • débit vidéo - 2500 Kbps;
  • codec audio - AAC;
  • débit binaire audio - 128 Kbps;
  • nombre de canaux - 2;
  • fréquence d'échantillonnage - 44,1 kHz.

La conversion de chaque vidéo originale a été effectuée cinq fois sur chacun des convertisseurs vidéo, à la fois en utilisant un GPU et uniquement un CPU. Après chaque conversion, l'ordinateur a redémarré.

En conséquence, chaque vidéo a été convertie dix fois dans chaque convertisseur vidéo. Pour automatiser ce travail de routine, un utilitaire spécial avec une interface graphique a été écrit pour automatiser entièrement le processus de test.

Configuration du banc d'essai

Le banc d'essai avait la configuration suivante :

  • processeur - Intel Core i7-3770K;
  • carte mère - Gigabyte GA-Z77X-UD5H;
  • chipset de la carte mère - Intel Z77 Express;
  • mémoire - DDR3-1600;
  • capacité de mémoire - 8 Go (deux modules GEIL, 4 Go chacun);
  • mode de fonctionnement de la mémoire - deux canaux;
  • carte vidéo - NVIDIA GeForce GTX 660Ti (pilote vidéo 314.07) ;
  • lecteur - Intel SSD 520 (240 Go).

Le système d'exploitation Windows 7 Ultimate (64 bits) a été installé sur le stand.

Initialement, nous avons testé le processeur et tous les autres composants du système en fonctionnement normal. Dans le même temps, le processeur Intel Core i7-3770K fonctionnait à une fréquence nominale de 3,5 GHz avec Turbo Boost activé (la fréquence maximale du processeur en mode Turbo Boost est de 3,9 GHz).

Ensuite, nous avons répété le processus de test, mais en overclockant le processeur à une fréquence fixe de 4,5 GHz (sans utiliser le mode Turbo Boost). Cela a permis de révéler la dépendance de la vitesse de conversion sur la fréquence du processeur (CPU).

À l'étape suivante des tests, nous sommes revenus aux paramètres standard du processeur et avons répété les tests avec d'autres cartes vidéo :

  • NVIDIA GeForce GTX 280 (pilote 314.07) ;
  • NVIDIA GeForce GTX 460 (pilote 314.07) ;
  • AMD Radeon HD6850 (pilote 13.1).

Ainsi, la conversion vidéo a été effectuée sur quatre cartes vidéo d'architectures différentes.

La carte graphique senior NVIDIA GeForce 660Ti est basée sur le processeur graphique du même nom avec la désignation de code GK104 (architecture Kepler), produit à l'aide d'une technologie de traitement 28 nm. Ce GPU contient 3,54 milliards de transistors et a une surface de matrice de 294 mm2.

Pour rappel, le GPU GK104 comprend quatre clusters de traitement graphique (GPC). Les clusters GPC sont des périphériques indépendants au sein du processeur et peuvent fonctionner comme des périphériques séparés, car ils disposent de toutes les ressources nécessaires : rastériseurs, moteurs de géométrie et unités de texture.

Chacun de ces clusters dispose de deux multiprocesseurs de streaming SMX (Streaming Multiprocessor), mais dans le processeur GK104 de l'un des clusters, un multiprocesseur est bloqué, il y a donc sept multiprocesseurs SMX au total.

Chaque multiprocesseur de streaming SMX contient 192 cœurs de calcul en continu (cœurs CUDA), donc au total, le processeur GK104 possède 1 344 cœurs de calcul CUDA. De plus, chaque multiprocesseur SMX contient 16 unités de texture (TMU), 32 unités de fonction spéciale (SFU), 32 unités de stockage de charge (LSU), un moteur PolyMorph et bien plus encore.

La carte graphique GeForce GTX 460 est basée sur un code GPU nommé GF104 basé sur l'architecture Fermi. Ce processeur est fabriqué à l'aide d'une technologie de processus de 40 nm et contient environ 1,95 milliard de transistors.

Le GPU GF104 comprend deux GPC. Chacun d'eux dispose de quatre multiprocesseurs SM en streaming, mais dans le processeur GF104 de l'un des clusters, un multiprocesseur est verrouillé, il n'y a donc que sept multiprocesseurs SM.

Chaque multiprocesseur de streaming SM contient 48 cœurs de calcul en continu (cœurs CUDA), de sorte que le GK104 dispose d'un total de 336 cœurs de calcul CUDA. De plus, chaque multiprocesseur SM contient huit unités de texture (TMU), huit unités de fonction spéciale (SFU), 16 unités de chargement et de stockage (LSU), un moteur PolyMorph, etc.

Le GPU GeForce GTX 280 est la deuxième génération de l'architecture GPU unifiée de NVIDIA, et son architecture est très différente de celle de Fermi et Kepler.

Le GPU GeForce GTX 280 est composé de clusters de traitement de texture (TPC), qui, bien que similaires, sont très différents des clusters GPC des architectures Fermi et Kepler. Il y a dix clusters de ce type dans le processeur GeForce GTX 280. Chaque cluster TPC comprend trois multiprocesseurs SM en streaming et huit unités d'échantillonnage et de filtrage de texture (TMU). Chaque multiprocesseur se compose de huit processeurs de flux (SP). Les multiprocesseurs contiennent également des blocs pour récupérer et filtrer les données de texture utilisées à la fois dans les graphiques et dans certaines tâches de calcul.

Ainsi, il y a 24 processeurs de flux dans un cluster TPC, et il y en a déjà 240 dans le GPU GeForce GTX 280.

Les caractéristiques récapitulatives des cartes vidéo utilisées dans les tests sur les GPU NVIDIA sont présentées dans le tableau.

Le tableau ci-dessus n'inclut pas la carte vidéo AMD Radeon HD6850, ce qui est assez naturel, car ses caractéristiques techniques sont difficiles à comparer avec les cartes vidéo NVIDIA. Par conséquent, nous l'examinerons séparément.

Le processeur graphique AMD Radeon HD6850, nom de code Barts, est fabriqué en 40 nm et contient 1,7 milliard de transistors.

L'architecture du processeur AMD Radeon HD6850 est une architecture unifiée avec une gamme de processeurs partagés pour diffuser plusieurs types de données.

Le processeur AMD Radeon HD6850 se compose de 12 cœurs SIMD, chacun contenant 16 processeurs de flux superscalaires et quatre unités de texture. Chaque processeur de flux superscalaire contient cinq processeurs de flux à usage général. Ainsi, il y a au total 12 * um * 16 * um * 5 = 960 processeurs de flux universels dans le GPU AMD Radeon HD6850.

La fréquence GPU de la carte graphique AMD Radeon HD6850 est de 775 MHz et la fréquence effective de la mémoire GDDR5 est de 4000 MHz. Dans ce cas, la quantité de mémoire est de 1024 Mo.

Résultats de test

Passons donc aux résultats des tests. Commençons par le premier test, lorsqu'une carte graphique NVIDIA GeForce GTX 660Ti est utilisée et qu'un processeur Intel Core i7-3770K fonctionne en mode normal.

En figue. 1-3 montrent les résultats de la conversion de trois vidéos de test par trois convertisseurs en modes avec et sans GPU.

Comme vous pouvez le voir sur les résultats des tests, l'effet de l'utilisation du GPU est évident. Pour Xilisoft Video Converter Ultimate 7.7.2, si un GPU est utilisé, le temps de conversion est réduit de 14, 9 et 19 % pour la première, la deuxième et la troisième vidéo, respectivement.

Pour Wondershare Video Converter Ultimate 6.0.32, l'utilisation du GPU peut réduire le temps de conversion de 10 %, 13 % et 23 % pour la première, la deuxième et la troisième vidéo, respectivement.

Mais Movavi Video Converter 10.2.1 gagne le plus de l'utilisation du GPU. Pour la première, la deuxième et la troisième vidéo, la réduction du temps de conversion est respectivement de 64, 81 et 41 %.

Il est clair que le gain de l'utilisation du GPU dépend à la fois de la vidéo d'origine et des paramètres de conversion vidéo, ce qui, en fait, est démontré par nos résultats.

Voyons maintenant quel sera le temps de conversion lorsque le processeur Intel Core i7-3770K sera overclocké à 4,5 GHz. Si nous supposons qu'en mode normal tous les cœurs de processeur sont chargés pendant la conversion et qu'en mode Turbo Boost fonctionnent à 3,7 GHz, alors une augmentation de la fréquence à 4,5 GHz correspond à un overclocking de 22%.

En figue. Les figures 4 à 6 montrent les résultats de la conversion de trois vidéos de test avec le processeur overclocké en modes avec et sans GPU. Dans ce cas, l'utilisation d'un processeur graphique permet de gagner en temps de conversion.

Pour Xilisoft Video Converter Ultimate 7.7.2, si un GPU est utilisé, le temps de conversion est réduit de 15, 9 et 20 % pour la première, la deuxième et la troisième vidéo, respectivement.

Pour Wondershare Video Converter Ultimate 6.0.32, l'utilisation du GPU peut réduire le temps de conversion de 10 %, 10 % et 20 % pour la première, la deuxième et la troisième vidéo, respectivement.

Pour Movavi Video Converter 10.2.1, l'utilisation du GPU peut réduire le temps de conversion de 59, 81 et 40 %, respectivement.

Naturellement, il est intéressant de voir comment l'overclocking du CPU peut réduire les temps de conversion avec et sans GPU.

En figue. Les figures 7 à 9 montrent les résultats de la comparaison du temps de conversion de clips vidéo sans utiliser de GPU en mode normal du processeur et en mode overclocking. Étant donné que dans ce cas la conversion est effectuée uniquement au moyen du CPU sans calcul sur le GPU, il est évident qu'une augmentation de la fréquence d'horloge du fonctionnement du processeur entraîne une réduction du temps de conversion (une augmentation de la vitesse de conversion ). Il est également évident que la réduction de la vitesse de conversion devrait être approximativement la même pour toutes les vidéos de test. Ainsi, pour Xilisoft Video Converter Ultimate 7.7.2, l'overclocking du processeur réduit le temps de conversion de 9, 11 et 9 % pour la première, la deuxième et la troisième vidéo, respectivement. Pour Wondershare Video Converter Ultimate 6.0.32, les temps de conversion sont respectivement réduits de 9, 9 et 10 % pour les première, deuxième et troisième vidéos. Et pour Movavi Video Converter 10.2.1, le temps de conversion est respectivement réduit de 13, 12 et 12%.

Ainsi, lorsque le processeur est overclocké de 20%, le temps de conversion est réduit d'environ 10%.

Comparons le temps de conversion des clips vidéo à l'aide du GPU en mode normal du processeur et en mode overclocking (Fig. 10-12).

Pour Xilisoft Video Converter Ultimate 7.7.2, l'overclocking du processeur réduit le temps de conversion de 10, 10 et 9 % pour les première, deuxième et troisième vidéos, respectivement. Pour Wondershare Video Converter Ultimate 6.0.32, le temps de conversion est réduit de 9, 6 et 5 % pour la première, la deuxième et la troisième vidéo, respectivement. Eh bien, pour Movavi Video Converter 10.2.1, le temps de conversion est respectivement réduit de 0,2, 10 et 10 %.

Comme vous pouvez le voir, pour les convertisseurs Xilisoft Video Converter Ultimate 7.7.2 et Wondershare Video Converter Ultimate 6.0.32, la réduction du temps de conversion lors de l'overclocking du processeur est à peu près la même à la fois en utilisant un GPU et sans l'utiliser, ce qui est logique , car ces convertisseurs ne sont pas très efficaces dans l'utilisation du GPU computing. Mais pour Movavi Video Converter 10.2.1, qui utilise efficacement le GPU computing, l'overclocking du processeur en mode GPU computing a peu d'effet sur la réduction du temps de conversion, ce qui est également compréhensible, puisque dans ce cas la charge principale incombe au GPU.

Voyons maintenant les résultats du test avec différentes cartes vidéo.

Il semblerait que plus la carte vidéo est puissante et plus il y a de cœurs CUDA (ou processeurs de flux universels pour les cartes vidéo AMD) dans le GPU, plus la conversion vidéo doit être efficace lors de l'utilisation du GPU. Mais dans la pratique, il s'avère que ce n'est pas tout à fait le cas.

En ce qui concerne les cartes vidéo basées sur les GPU NVIDIA, la situation est la suivante. Lors de l'utilisation des convertisseurs Xilisoft Video Converter Ultimate 7.7.2 et Wondershare Video Converter Ultimate 6.0.32, le temps de conversion ne dépend pratiquement pas du type de carte vidéo utilisée. C'est-à-dire que pour les cartes vidéo NVIDIA GeForce GTX 660Ti, NVIDIA GeForce GTX 460 et NVIDIA GeForce GTX 280 en mode d'utilisation des calculs sur le GPU, le temps de conversion est le même (Fig. 13-15).

Riz. 1. Résultats de conversion du premier
tester la vidéo en mode normal
travail du processeur

processeur cartes graphiques en mode d'utilisation GPU

Riz. 14. Résultats de la comparaison du temps de conversion de la deuxième vidéo

Riz. 15. Résultats de la comparaison du temps de conversion de la troisième vidéo
sur diverses cartes vidéo en mode d'utilisation du GPU

Cela ne peut s'expliquer que par le fait que l'algorithme de calcul sur le GPU, implémenté dans Xilisoft Video Converter Ultimate 7.7.2 et Wondershare Video Converter Ultimate 6.0.32, est tout simplement inefficace et ne permet pas d'utiliser activement tous les cœurs graphiques. D'ailleurs, cela explique le fait que pour ces convertisseurs la différence de temps de conversion dans les modes d'utilisation du GPU et sans l'utiliser soit faible.

Dans Movavi Video Converter 10.2.1, la situation est quelque peu différente. On s'en souvient, ce convertisseur est capable d'une utilisation très efficace du GPU computing, et donc, en mode GPU, le temps de conversion dépend du type de carte vidéo utilisé.

Mais avec la carte vidéo AMD Radeon HD 6850, tout est comme d'habitude. Soit le pilote de la carte vidéo "courbe", soit les algorithmes implémentés dans les convertisseurs nécessitent une révision sérieuse, mais dans le cas de l'utilisation de calculs sur le GPU, les résultats ne s'améliorent pas ou ne se détériorent pas.

Plus précisément, la situation est la suivante. Pour Xilisoft Video Converter Ultimate 7.7.2, lors de l'utilisation du GPU pour convertir la première vidéo de test, le temps de conversion augmente de 43%, lors de la conversion de la deuxième vidéo - de 66%.

De plus, le Xilisoft Video Converter Ultimate 7.7.2 se caractérise également par l'instabilité des résultats. L'écart de temps de conversion peut aller jusqu'à 40% ! C'est pourquoi nous avons répété tous les tests dix fois et calculé le résultat moyen.

Mais pour les convertisseurs Wondershare Video Converter Ultimate 6.0.32 et Movavi Video Converter 10.2.1, lors de l'utilisation du GPU pour convertir les trois vidéos, le temps de conversion ne change pas du tout ! Il est probable que Wondershare Video Converter Ultimate 6.0.32 et Movavi Video Converter 10.2.1 n'utilisent pas la technologie AMD APP lors de la conversion, ou que le pilote vidéo AMD soit simplement « tordu », à la suite de quoi la technologie AMD APP le fait. ne fonctionne pas.

conclusions

Sur la base des tests effectués, les conclusions importantes suivantes peuvent être tirées. Dans les convertisseurs vidéo modernes, la technologie de calcul GPU peut en effet être utilisée, ce qui permet d'augmenter la vitesse de conversion. Cependant, cela ne signifie pas du tout que tous les calculs sont complètement transférés au GPU et que le CPU reste inactif. Comme le montrent les tests, lors de l'utilisation de la technologie GPGPU, le processeur central reste chargé, ce qui signifie que l'utilisation de puissants processeurs centraux multicœurs dans les systèmes utilisés pour la conversion vidéo reste pertinente. L'exception à cette règle est la technologie AMD APP sur les GPU AMD. Par exemple, lors de l'utilisation de Xilisoft Video Converter Ultimate 7.7.2 avec la technologie AMD APP activée, la charge CPU est vraiment réduite, mais cela conduit au fait que le temps de conversion ne diminue pas, mais au contraire augmente.

En général, si nous parlons de conversion de vidéo avec une utilisation supplémentaire du GPU, il est conseillé d'utiliser des cartes vidéo avec des GPU NVIDIA pour résoudre ce problème. Comme le montre la pratique, ce n'est que dans ce cas qu'il est possible d'augmenter la vitesse de conversion. Et vous devez vous rappeler que le gain réel en vitesse de conversion dépend de nombreux facteurs. Ce sont les formats vidéo d'entrée et de sortie et, bien sûr, le convertisseur vidéo lui-même. Les convertisseurs Xilisoft Video Converter Ultimate 7.7.2 et Wondershare Video Converter Ultimate 6.0.32 sont mal adaptés à cette tâche, mais le convertisseur et Movavi Video Converter 10.2.1 sont capables d'utiliser très efficacement les capacités du GPU NVIDIA.

Quant aux cartes vidéo basées sur les GPU AMD, elles ne doivent pas du tout être utilisées pour des tâches de conversion vidéo. Dans le meilleur des cas, cela n'augmentera pas la vitesse de conversion, et dans le pire des cas, vous pourrez la réduire.

Caractéristiques de l'architecture AMD/ATI Radeon

Cela s'apparente à la naissance de nouvelles espèces biologiques, lorsque, lors de la maîtrise des habitats, les êtres vivants évoluent pour améliorer leur adaptabilité à l'environnement. Ainsi, le GPU, en commençant par l'accélération de la rastérisation et de la texturation des triangles, a développé des capacités supplémentaires pour exécuter des programmes de shader pour colorer ces mêmes triangles. Et ces capacités étaient également recherchées dans l'informatique non graphique, où, dans certains cas, elles offrent des gains de performances significatifs par rapport aux solutions traditionnelles.

Tirons plus loin les analogies - après une longue évolution sur terre, les mammifères sont entrés dans la mer, où ils ont expulsé la vie marine ordinaire. Dans la lutte compétitive, les mammifères ont utilisé à la fois de nouvelles capacités avancées apparues à la surface de la terre et spécialement acquises pour s'adapter à la vie dans l'eau. De même, les GPU, basés sur les avantages de l'architecture pour les graphiques 3D, acquièrent de plus en plus de fonctionnalités spéciales utiles pour effectuer des tâches non graphiques.

Alors, qu'est-ce qui permet au GPU de revendiquer son propre secteur dans l'arène des logiciels polyvalents ? La microarchitecture du GPU est construite d'une manière complètement différente de celle des CPU classiques, et elle présente certains avantages dès le départ. Les tâches graphiques impliquent un traitement parallèle indépendant et le GPU est multithread par conception. Mais ce parallélisme n'est qu'une joie pour lui. La microarchitecture est conçue pour tirer parti du grand nombre de threads disponibles sur le marché.

Le GPU est composé de plusieurs dizaines (30 pour Nvidia GT200, 20 pour Evergreen, 16 pour Fermi) cœurs de processeur, appelés Streaming Multiprocessor dans la terminologie de Nvidia, et SIMD Engine dans la terminologie d'ATI. Pour les besoins de cet article, nous les appellerons des miniprocesseurs, car ils exécutent plusieurs centaines de threads et peuvent faire presque tout ce qu'un processeur ordinaire peut faire, mais pas tout.

Les noms marketing prêtent à confusion - ils indiquent, pour plus d'importance, le nombre de modules fonctionnels qui peuvent soustraire et se multiplier : par exemple, 320 "cœurs" vectoriels (cœurs). Ces grains ressemblent plus à des grains. Il est préférable de considérer un GPU comme une sorte de processeur multicœur avec un grand nombre de cœurs exécutant plusieurs threads en même temps.

Chaque miniprocesseur dispose d'une mémoire locale, 16 Ko pour le GT200, 32 Ko pour l'Evergreen et 64 Ko pour le Fermi (essentiellement un cache L1 programmable). Il a des temps d'accès similaires au cache L1 d'un processeur ordinaire et exécute des fonctions similaires de livraison de données la plus rapide aux modules fonctionnels. Dans l'architecture Fermi, une partie de la mémoire locale peut être configurée comme un cache normal. Dans le GPU, la mémoire locale est utilisée pour un échange de données rapide entre les threads d'exécution. L'un des schémas habituels d'un programme GPU est le suivant : tout d'abord, les données de la mémoire globale du GPU sont chargées dans la mémoire locale. Il s'agit simplement d'une mémoire vidéo ordinaire, située (comme la mémoire système) séparément de "son" processeur - dans le cas de la vidéo, elle est soudée par plusieurs microcircuits sur le textolite de la carte vidéo. Ensuite, plusieurs centaines de threads travaillent avec ces données dans la mémoire locale et écrivent le résultat dans la mémoire globale, après quoi il est transféré à la CPU. Il est de la responsabilité du programmeur d'écrire des instructions pour le chargement et le déchargement des données de la mémoire locale. Il s'agit essentiellement du partitionnement des données [d'une tâche spécifique] pour un traitement parallèle. Le GPU prend également en charge les instructions d'écriture / lecture atomique en mémoire, mais elles sont inefficaces et sont généralement nécessaires à l'étape finale pour "coller" les résultats des calculs de tous les miniprocesseurs.

La mémoire locale est commune à tous les threads s'exécutant dans le miniprocesseur, donc, par exemple, dans la terminologie de Nvidia, elle est même appelée partagée, et le terme mémoire locale dénote exactement le contraire, à savoir : une certaine zone personnelle d'un thread séparé dans mémoire globale, visible et accessible à lui seul. Mais en plus de la mémoire locale, le miniprocesseur a une autre zone mémoire, qui dans toutes les architectures est environ quatre fois plus grande. Il est divisé également entre tous les threads d'exécution ; ce sont des registres pour stocker les variables et les résultats intermédiaires des calculs. Il y a plusieurs dizaines de registres par thread. Le nombre exact dépend du nombre de threads exécutés par le miniprocesseur. Ce nombre est très important, car la latence de la mémoire globale est très élevée, des centaines de cycles d'horloge, et en l'absence de caches, il n'y a nulle part où stocker les résultats de calcul intermédiaires.

Et une autre caractéristique importante du GPU : le vecteur "soft". Chaque miniprocesseur dispose d'un grand nombre de modules de calcul (8 pour GT200, 16 pour Radeon et 32 ​​pour Fermi), mais ils ne peuvent tous exécuter qu'une même instruction, avec une seule adresse de programme. Dans ce cas, les opérandes peuvent être différents, différents threads ont le leur. Par exemple, l'instruction additionner le contenu de deux registres: il est exécuté simultanément par tous les appareils informatiques, mais les registres sont pris différents. On suppose que tous les threads d'un programme GPU, tout en effectuant un traitement de données parallèle, se déplacent généralement en parallèle dans le code du programme. Ainsi, tous les modules de calcul sont chargés uniformément. Et si les threads divergent dans leur chemin d'exécution du code en raison de branches dans le programme, alors la soi-disant sérialisation se produit. Ensuite, tous les modules de calcul ne sont pas utilisés, car les threads reçoivent diverses instructions d'exécution, et un bloc de modules de calcul ne peut exécuter, comme nous l'avons déjà dit, qu'une instruction avec une seule adresse. Et, bien sûr, les performances diminuent par rapport au maximum.

L'avantage est que la vectorisation est entièrement automatique, ce n'est pas de la programmation avec SSE, MMX, etc. Et le GPU gère lui-même les écarts. Théoriquement, il est possible d'écrire des programmes pour le GPU, sans penser à la nature vectorielle des modules d'exécution, mais la vitesse d'un tel programme ne sera pas très élevée. L'inconvénient est la grande largeur du vecteur. C'est plus que le nombre nominal de modules fonctionnels et est de 32 pour le GPU Nvidia et de 64 pour la Radeon. Les threads sont traités par blocs de la taille appropriée. Nvidia appelle ce bloc de threads warp, AMD - wave front, ce qui revient au même. Ainsi, sur 16 dispositifs informatiques, un « front d'onde » de 64 threads est traité en quatre cycles d'horloge (en supposant la longueur d'instruction habituelle). L'auteur préfère le terme de chaîne dans ce cas, en raison de l'association avec le terme nautique de chaîne, qui désigne une corde nouée à partir de cordes torsadées. Ainsi, les fils « se tordent » et forment un faisceau intégral. Mais le « front de vague » peut aussi être associé à la mer : les consignes arrivent aux actionneurs au moment où les vagues déferlent les unes après les autres sur le rivage.

Si tous les threads sont également avancés dans l'exécution du programme (sont au même endroit) et, par conséquent, exécutent une instruction, alors tout va bien, mais sinon, un ralentissement se produit. Dans ce cas, les threads du même front de chaîne ou d'onde sont situés à différents endroits du programme, ils sont divisés en groupes de threads avec la même valeur du numéro d'instruction (en d'autres termes, le pointeur d'instruction). Et comme auparavant, seuls les threads du même groupe sont exécutés à la fois - tous exécutent la même instruction, mais avec des opérandes différents. En conséquence, warp est exécuté autant de fois plus lentement qu'il est divisé en groupes, et le nombre de threads dans un groupe n'a pas d'importance. Même si le groupe se compose d'un seul fil, cela prendra toujours le même temps qu'une chaîne complète. Dans le matériel, cela est mis en œuvre en masquant certains threads, c'est-à-dire que les instructions sont formellement exécutées, mais les résultats de leur exécution ne sont enregistrés nulle part et ne sont pas utilisés à l'avenir.

Bien qu'à un instant donné chaque miniprocesseur (Streaming MultiProcessor ou SIMD Engine) exécute des instructions appartenant à un seul warp (un paquet de threads), il a plusieurs dizaines de warps actifs dans le pool d'exécutables. Après avoir exécuté les instructions d'un warp, le miniprocesseur n'exécute pas l'instruction suivante des threads du warp donné, mais les instructions du warp de quelqu'un d'autre. Cette chaîne peut être à un endroit complètement différent dans le programme, cela n'affectera pas la vitesse, car ce n'est qu'à l'intérieur de la chaîne, les instructions de tous les threads doivent être les mêmes pour une exécution à pleine vitesse.

Dans ce cas, chacun des 20 moteurs SIMD a quatre fronts d'onde actifs, chacun avec 64 threads. Chaque brin est indiqué par une ligne courte. Total : 64 x 4 x 20 = 5120 brins

Ainsi, étant donné que chaque chaîne ou front d'onde se compose de 32 à 64 threads, le miniprocesseur a plusieurs centaines de threads actifs qui sont exécutés presque simultanément. Ci-dessous, nous verrons les avantages architecturaux d'un si grand nombre de threads parallèles, mais d'abord, examinons les limites des miniprocesseurs GPU.

L'essentiel est que le GPU n'ait pas de pile où les paramètres de fonction et les variables locales pourraient être stockés. En raison du grand nombre de threads pour la pile, il n'y a tout simplement pas de place sur la matrice. En effet, étant donné que le GPU exécute simultanément environ 10 000 threads, avec une seule taille de pile de threads de 100 Ko, le volume total sera de 1 Go, ce qui équivaut au volume standard de toute la mémoire vidéo. De plus, il n'y a aucun moyen de mettre une pile de taille significative dans le cœur du GPU lui-même. Par exemple, si vous mettez 1000 octets de pile par thread, alors un seul miniprocesseur nécessitera 1 Mo de mémoire, ce qui représente près de cinq fois la quantité totale de mémoire locale du miniprocesseur et la mémoire allouée au stockage des registres.

Par conséquent, il n'y a pas de récursivité dans un programme GPU, et vous ne pouvez pas vraiment vous déplacer avec les appels de fonction. Toutes les fonctions sont directement substituées dans le code lorsque le programme est compilé. Cela limite la portée des GPU aux tâches de type informatique. Il est parfois possible d'utiliser une émulation de pile limitée en utilisant la mémoire globale pour les algorithmes de récursivité avec une faible profondeur d'itération connue, mais ce n'est pas une application GPU typique. Pour ce faire, il est nécessaire de développer spécialement un algorithme, d'étudier la possibilité de sa mise en œuvre sans garantie d'accélération réussie par rapport au CPU.

Pour la première fois, Fermi a la possibilité d'utiliser des fonctions virtuelles, mais encore une fois leur utilisation est limitée par l'absence d'un grand cache rapide pour chaque thread. 1536 threads représentent 48 Ko ou 16 Ko L1, c'est-à-dire que les fonctions virtuelles du programme peuvent être utilisées relativement rarement, sinon une mémoire globale lente sera également utilisée pour la pile, ce qui ralentira l'exécution et, très probablement, n'apportera pas avantages par rapport à la version CPU.

Ainsi, le GPU est représenté comme un coprocesseur de calcul, dans lequel les données sont chargées, elles sont traitées par un algorithme et le résultat est renvoyé.

Avantages de l'architecture

Mais le GPU compte très vite. Et en cela, il est aidé par son multithreading élevé. Le grand nombre de threads actifs permet de masquer en partie la latence élevée de la mémoire vidéo globale située séparément, qui est d'environ 500 cycles d'horloge. Il est particulièrement bien nivelé pour le code avec une forte densité d'opérations arithmétiques. Ainsi, la coûteuse hiérarchie de cache L1-L2-L3 n'est pas requise. Au lieu de cela, de nombreux modules de calcul peuvent être placés sur la puce, offrant des performances arithmétiques exceptionnelles. Pendant ce temps, les instructions d'un thread ou d'une chaîne sont exécutées, les centaines de threads restants attendent tranquillement leurs données.

Fermi a introduit un cache de second niveau d'environ 1 Mo, mais il n'est pas comparable aux caches des processeurs modernes, il est plutôt destiné à la communication entre les cœurs et diverses astuces logicielles. Si sa taille est divisée entre toutes les dizaines de milliers de threads, chacun aura un volume absolument insignifiant.

Mais outre la latence de la mémoire globale, il y a beaucoup plus de latences dans le périphérique informatique qui doivent être masquées. Il s'agit de la latence du transfert de données à l'intérieur du cristal depuis les appareils informatiques vers le cache de premier niveau, c'est-à-dire la mémoire locale du GPU, et vers les registres, ainsi que le cache d'instructions. Le fichier registre, comme la mémoire locale, sont situés séparément des modules fonctionnels, et la vitesse d'accès à ceux-ci est d'environ une douzaine et demie de cycles d'horloge. Et encore, un grand nombre de threads, warps actifs, masquent efficacement cette latence. De plus, la bande passante totale (bande passante) d'accès à la mémoire locale de l'ensemble du GPU, compte tenu du nombre de miniprocesseurs, est bien supérieure à la bande passante d'accès au cache de premier niveau dans les processeurs modernes. Le GPU peut traiter beaucoup plus de données par unité de temps.

On peut tout de suite dire que si le GPU n'est pas fourni avec un grand nombre de threads parallèles, alors il aura des performances presque nulles, car il fonctionnera au même rythme, comme s'il était complètement chargé, et effectuera beaucoup moins de travail. Par exemple, ne laissez qu'un seul thread au lieu de 10 000 : les performances diminueront d'environ un millier de fois, car non seulement tous les blocs ne seront pas chargés, mais toutes les latences affecteront également.

Le problème de la dissimulation de la latence est également aigu pour les processeurs haute fréquence modernes ; des méthodes sophistiquées sont utilisées pour l'éliminer - pipeline profond, exécution désordonnée des instructions. Cela nécessite des ordonnanceurs d'exécution d'instructions complexes, divers tampons, etc., qui prennent de la place sur la puce. Tout cela est nécessaire pour obtenir les meilleures performances en mode monothread.

Mais pour le GPU, tout cela n'est pas nécessaire, c'est architecturalement plus rapide pour les tâches de calcul avec un grand nombre de threads. Mais il convertit le multithreading en performance, comme la pierre philosophale transforme le plomb en or.

Le GPU a été conçu à l'origine pour exécuter de manière optimale des programmes de shader pour les pixels triangulaires, qui sont évidemment indépendants et peuvent être exécutés en parallèle. Et à partir de cet état, il a évolué en ajoutant diverses capacités (mémoire locale et accès adressable à la mémoire vidéo, ainsi qu'en compliquant le jeu d'instructions) en un dispositif informatique très puissant, qui ne peut encore être appliqué efficacement que pour des algorithmes permettant une implémentation hautement parallèle. utilisant une quantité limitée de mémoire locale.

Exemple

L'une des tâches les plus classiques pour les GPU est la tâche de calculer l'interaction de N corps qui créent un champ gravitationnel. Mais si, par exemple, nous devons calculer l'évolution du système Terre-Lune-Soleil, alors le GPU est une mauvaise aide pour nous : peu d'objets. Pour chaque objet, il est nécessaire de calculer les interactions avec tous les autres objets, et il n'y en a que deux. Dans le cas du mouvement du système solaire avec toutes les planètes et leurs lunes (environ quelques centaines d'objets), le GPU n'est toujours pas très efficace. Cependant, un processeur multicœur, en raison de la surcharge élevée pour le contrôle des threads, ne sera pas non plus en mesure de montrer toute sa puissance, il fonctionnera en mode monothread. Mais s'il est également nécessaire de calculer les trajectoires des comètes et des objets dans la ceinture d'astéroïdes, alors c'est déjà une tâche pour le GPU, car il y a suffisamment d'objets pour créer le nombre requis de fils de calcul parallèles.

Le GPU est également bon pour calculer la collision d'amas globulaires de centaines de milliers d'étoiles.

Une autre opportunité d'utiliser la puissance du GPU dans le problème à N corps apparaît lorsqu'il est nécessaire de calculer de nombreuses tâches distinctes, bien qu'avec un petit nombre de corps. Par exemple, s'il est nécessaire de calculer les variantes d'évolution d'un système pour différentes variantes des vitesses initiales. Il sera alors possible d'utiliser efficacement le GPU sans problème.

Détails de la microarchitecture AMD Radeon

Nous avons examiné les principes de base de l'organisation des GPU, ils sont communs aux accélérateurs vidéo de tous les fabricants, car ils avaient initialement une tâche cible - les programmes de shader. Cependant, les fabricants ont trouvé l'occasion de ne pas être d'accord sur les détails de la mise en œuvre de la microarchitecture. Bien que les CPU de différents fournisseurs soient parfois très différents, voire compatibles, comme par exemple Pentium 4 et Athlon ou Core. L'architecture de Nvidia étant déjà largement connue, nous allons maintenant nous intéresser à Radeon et mettre en évidence les principales différences dans les approches de ces éditeurs.

Les cartes graphiques AMD ont reçu un support complet pour l'informatique à usage général, à commencer par la famille Evergreen, qui a également introduit pour la première fois la spécification DirectX 11. Les cartes de la famille 47xx ont un certain nombre de limitations importantes, qui seront discutées ci-dessous.

Les différences de taille de la mémoire locale (32 Ko pour la Radeon contre 16 Ko pour la GT200 et 64 Ko pour la Fermi) ne sont généralement pas fondamentales. Ainsi que la taille du front d'onde de 64 threads pour AMD contre 32 threads pour Warp de Nvidia. Presque tous les programmes GPU peuvent être facilement reconfigurés et modifiés pour ces paramètres. Les performances peuvent changer de plusieurs dizaines de pour cent, mais dans le cas d'un GPU, ce n'est pas si important, car un programme GPU s'exécute généralement dix fois plus lentement qu'un analogique pour un CPU, ou dix fois plus vite, ou ne fonctionne pas du tout.

L'utilisation par AMD de la technologie VLIW (Very Long Instruction Word) est plus importante. Nvidia utilise des instructions scalaires simples qui fonctionnent sur des registres scalaires. Ses accélérateurs implémentent un RISC classique simple. Les cartes vidéo AMD ont le même nombre de registres que GT200, mais les registres vectoriels sont de 128 bits. Chaque instruction VLIW fonctionne avec plusieurs registres 32 bits à quatre composants, ce qui est similaire à SSE, mais les capacités de VLIW sont beaucoup plus larges. Ce n'est pas SIMD (Single Instruction Multiple Data) comme SSE - ici les instructions pour chaque paire d'opérandes peuvent être différentes et même dépendantes ! Par exemple, appelons les composantes du registre A a1, a2, a3, a4 ; pour le registre B - le même. Peut être calculé avec une seule instruction qui s'exécute en un cycle d'horloge, par exemple, a1 × b1 + a2 × b2 + a3 × b3 + a4 × b4 ou un vecteur bidimensionnel (a1 × b1 + a2 × b2, a3 × b3 + a4 × b4 ).

Ceci est rendu possible par une fréquence GPU inférieure à celle d'un CPU et une réduction spectaculaire de la technologie des processus ces dernières années. Dans ce cas, aucun ordonnanceur n'est requis, presque tout est exécuté dans un cycle.

Grâce aux instructions vectorielles, les performances maximales de la Radeon en nombres en simple précision sont très élevées au téraflops.

Un registre vectoriel peut stocker un nombre en double précision au lieu de quatre nombres en simple précision. Et une instruction VLIW peut soit ajouter deux paires de doubles, soit multiplier deux nombres, soit multiplier deux nombres et ajouter au troisième. Ainsi, la performance de pointe en double est environ cinq fois inférieure à celle en float. Pour les anciens modèles Radeon, il correspond aux performances de Nvidia Tesla sur la nouvelle architecture Fermi et est bien supérieur aux performances des cartes doubles sur l'architecture GT200. Les cartes graphiques grand public Geforce basées sur Fermi ont quadruplé la vitesse maximale de double calcul.


Schéma de principe du fonctionnement de Radeon. Présenté un seul miniprocesseur sur 20 fonctionnant en parallèle

Les fabricants de GPU, contrairement aux fabricants de CPU (principalement compatibles x86), ne sont pas liés par des problèmes de compatibilité. Un programme GPU est d'abord compilé dans un code intermédiaire, et lorsque le programme est exécuté, le pilote compile ce code en instructions machine spécifiques au modèle. Comme décrit ci-dessus, les fabricants de GPU en ont profité en proposant une ISA (Instruction Set Architecture) pratique pour leurs GPU et en les modifiant de génération en génération. Dans tous les cas, cela a ajouté un certain pourcentage de performance en raison de l'absence (comme inutile) d'un décodeur. Mais AMD est allé encore plus loin en proposant son propre format pour l'emplacement des instructions dans le code machine. Ils ne sont pas organisés de manière séquentielle (selon la liste des programmes), mais en sections.

Il y a d'abord une section d'instructions de branchement conditionnel, qui ont des liens vers des sections d'instructions arithmétiques continues correspondant aux différentes branches des branches. Ils sont appelés bundles VLIW. Ces sections contiennent uniquement des instructions arithmétiques avec des données provenant de registres ou de la mémoire locale. Cette organisation simplifie le contrôle du flux des instructions et leur livraison aux unités d'exécution. Ceci est d'autant plus utile que les instructions VLIW sont relativement volumineuses. Il existe également des sections pour les instructions d'accès à la mémoire.

Sections d'instructions de saut conditionnel
Rubrique 0Branchement 0Lien vers la section # 3 des instructions arithmétiques continues
Section 1Branchement 1Lien vers la section #4
Section 2Branchement 2Lien vers la rubrique #5
Sections d'instructions arithmétiques continues
Section 3Instruction VLIW 0VLIW Instruction 1VLIW Instruction 2VLIW Instruction 3
Section 4VLIW Instruction 4Instruction VLIW 5
Article 5VLIW Instruction 6VLIW Instruction 7VLIW Instruction 8VLIW Instruction 9

Les GPU des deux fabricants (Nvidia et AMD) ont également des instructions intégrées pour un calcul rapide en quelques cycles d'horloge de fonctions mathématiques de base, racine carrée, exposant, logarithmes, sinus et cosinus pour les nombres à simple précision. Il existe des unités de calcul spéciales pour cela. Ils sont « nés » de la nécessité d'implémenter une approximation rapide de ces fonctions dans les shaders géométriques.

Même si quelqu'un ne savait pas que les GPU sont utilisés pour les graphiques et ne se familiarisait qu'avec les caractéristiques techniques, alors, sur cette base, il pourrait deviner que ces coprocesseurs informatiques provenaient d'accélérateurs vidéo. De même, par certains traits des mammifères marins, les scientifiques ont compris que leurs ancêtres étaient des créatures terrestres.

Mais une caractéristique plus évidente qui trahit l'origine graphique de l'appareil est les unités de lecture de texture 2D et 3D avec prise en charge de l'interpolation bilinéaire. Ils sont largement utilisés dans les programmes GPU, car ils permettent une lecture plus rapide et plus facile des tableaux de données en lecture seule. L'une des options standard pour le comportement d'une application GPU est de lire des tableaux de données initiales, de les traiter dans des cœurs de calcul et d'écrire le résultat dans un autre tableau, qui est ensuite transféré vers le CPU. Ce schéma est standard et commun car il est pratique pour l'architecture GPU. Les tâches qui nécessitent une lecture et une écriture intensives dans une grande zone de mémoire globale, contenant ainsi des dépendances de données, sont difficiles à paralléliser et à mettre en œuvre efficacement sur le GPU. De plus, leurs performances dépendront grandement de la latence globale de la mémoire, qui est très élevée. Mais si la tâche est décrite par le modèle "lecture des données - traitement - écriture du résultat", alors vous pouvez presque certainement obtenir un grand coup de pouce de son exécution sur le GPU.

Pour les données de texture dans le GPU, il existe une hiérarchie distincte de petits caches des premier et deuxième niveaux. C'est elle qui fournit l'accélération à partir de l'utilisation des textures. Cette hiérarchie est apparue à l'origine dans les GPU afin de tirer parti de la localisation de la texture : évidemment, après avoir traité un pixel pour un pixel voisin (avec une forte probabilité), des données de texture rapprochées sont nécessaires. Mais de nombreux algorithmes pour l'informatique conventionnelle ont une nature similaire d'accès aux données. Les caches de textures à partir de graphiques seront donc très utiles.

Bien que la taille des caches L1-L2 dans les cartes Nvidia et AMD soit à peu près similaire, ce qui est évidemment dû aux exigences d'optimalité du point de vue des graphismes du jeu, la latence d'accès à ces caches diffère sensiblement. La latence d'accès de Nvidia est plus élevée et les caches de texture dans Geforce aident principalement à réduire la charge sur le bus mémoire, et non à accélérer directement l'accès aux données. Ce n'est pas visible dans les programmes graphiques, mais c'est important pour les programmes à usage général. Dans Radeon, la latence du cache de texture est plus faible, mais la latence de la mémoire locale du miniprocesseur est plus élevée. Un exemple peut être donné : pour une multiplication matricielle optimale sur les cartes Nvidia, il vaut mieux utiliser de la mémoire locale, en y chargeant la matrice bloc par bloc, et pour AMD, il vaut mieux s'appuyer sur un cache de texture à faible latence, en lisant les éléments matriciels comme requis. Mais c'est déjà une optimisation assez subtile, et pour l'algorithme déjà fondamentalement transféré au GPU.

Cette différence apparaît également lors de l'utilisation de textures 3D. L'un des premiers benchmarks pour le GPU computing, qui montrait un sérieux avantage pour AMD, n'utilisait que des textures 3D, puisqu'il fonctionnait avec un tableau de données en trois dimensions. Et la latence d'accès aux textures dans Radeon est beaucoup plus rapide, et le boîtier 3D est en plus plus optimisé en hardware.

Pour obtenir les performances maximales du matériel de diverses entreprises, vous avez besoin d'un réglage de l'application pour une carte spécifique, mais c'est un ordre de grandeur moins important que, en principe, le développement d'un algorithme pour l'architecture GPU.

Limitations de la série Radeon 47xx

La prise en charge du GPU est incomplète dans cette famille. Trois points importants peuvent être relevés. Premièrement, il n'y a pas de mémoire locale, c'est-à-dire qu'elle est physiquement présente, mais elle n'a pas la capacité d'accès universel requise par la norme moderne pour les programmes GPU. Il est émulé par programmation dans la mémoire globale, c'est-à-dire que son utilisation, contrairement à un GPU complet, n'apportera aucun avantage. Le deuxième point est la prise en charge limitée de diverses instructions pour les opérations de mémoire atomique et les instructions de synchronisation. Et le troisième point est la taille assez réduite du cache d'instructions : à partir d'une certaine taille du programme, la vitesse ralentit considérablement. Il existe également d'autres restrictions mineures. On peut dire que seuls les programmes parfaitement adaptés au GPU fonctionneront bien sur cette carte vidéo. Soit des programmes de test simples qui ne fonctionnent qu'avec des registres, une carte vidéo peut afficher de bons résultats en gigaflops, il est problématique de programmer efficacement quelque chose de complexe pour elle.

Avantages et inconvénients Evergreen

En comparant les produits AMD et Nvidia, en termes de GPU computing, la série 5xxx ressemble à un GT200 très puissant. Si puissant qu'en performance de pointe, il surpasse Fermi d'environ deux fois et demie. Surtout après la réduction des paramètres des nouvelles cartes graphiques Nvidia, le nombre de cœurs a été réduit. Mais l'avènement du cache L2 dans Fermi simplifie l'implémentation de certains algorithmes sur le GPU, élargissant ainsi le champ d'application du GPU. Fait intéressant, pour les programmes CUDA, bien optimisés pour la génération précédente de GT200, les innovations architecturales de Fermi n'ont souvent rien donné. Ils ont accéléré proportionnellement à l'augmentation du nombre de modules de calcul, c'est-à-dire moins du double (pour les nombres en simple précision), voire moins, car la bande passante mémoire n'a pas augmenté (ou pour d'autres raisons).

Et dans les tâches qui s'intègrent bien à l'architecture GPU et ont une nature vectorielle prononcée (par exemple, la multiplication de matrices), Radeon affiche des performances relativement proches du pic théorique et surpasse Fermi. Sans parler des processeurs multicœurs. Surtout dans les problèmes avec des nombres à simple précision.

Mais Radeon a une zone de matrice plus petite, moins de dissipation thermique, moins de consommation d'énergie, un rendement plus élevé et, par conséquent, un coût inférieur. Et directement dans les problèmes graphiques 3D, le gain de Fermi, le cas échéant, est bien inférieur à la différence de surface cristalline. Cela est largement dû au fait que l'architecture informatique Radeon avec 16 périphériques informatiques par miniprocesseur, une taille de front d'onde de 64 threads et des instructions vectorielles VLIW est excellente pour sa tâche principale - le calcul des shaders graphiques. Pour la grande majorité des utilisateurs ordinaires, les performances de jeu et le prix sont une priorité.

En termes de programmes scientifiques professionnels, l'architecture Radeon offre le meilleur rapport qualité-prix, des performances par watt et des performances absolues dans des tâches qui correspondent en principe bien à l'architecture GPU, permettent la parallélisation et la vectorisation.

Par exemple, dans une tâche de sélection de clé complètement parallèle et facilement vectorisée, Radeon est plusieurs fois plus rapide que Geforce et plusieurs dizaines de fois plus rapide que CPU.

Cela correspond au concept général d'AMD Fusion, selon lequel les GPU devraient compléter le CPU, et à l'avenir être intégrés dans le cœur du CPU lui-même, comme auparavant le coprocesseur mathématique était transféré d'une matrice séparée au cœur du processeur (cela s'est produit vingt il y a quelques années, avant l'apparition des premiers processeurs Pentium). Le GPU sera un cœur graphique intégré et un coprocesseur vectoriel pour les tâches de streaming.

Radeon utilise une technique astucieuse consistant à mélanger les instructions de différents fronts d'onde lorsqu'elles sont exécutées par des modules fonctionnels. C'est facile à faire car les instructions sont complètement indépendantes. Le principe est similaire à l'exécution en pipeline d'instructions indépendantes sur les processeurs modernes. Apparemment, cela permet d'exécuter efficacement des instructions vectorielles VLIW complexes qui occupent de nombreux octets. Dans un processeur, cela nécessite un planificateur sophistiqué pour identifier les instructions indépendantes ou l'utilisation de la technologie Hyper-Threading, qui alimente également le processeur avec des instructions notoirement indépendantes de différents threads.

horloge 0étape 1étape 2étape 3étape 4étape 5étape 6étape 7Module VLIW
front d'onde 0front d'onde 1front d'onde 0front d'onde 1front d'onde 0front d'onde 1front d'onde 0front d'onde 1
instr. 0instr. 0instr. 16instr. 16instr. 32instr. 32instr. 48instr. 48VLIW0
instr. 1VLIW1
instr. 2VLIW2
instr. 3VLIW3
instr. 4VLIW4
instr. 5VLIW5
instr. 6VLIW6
instr. 7VLIW7
instr. huitVLIW8
instr. neufVLIW9
instr. DixVLIW10
instr. OnzeVLIW11
instr. 12VLIW12
instr. 13VLIW13
instr. QuatorzeVLIW14
instr. 15VLIW15

128 instructions de deux fronts d'onde, chacune composée de 64 opérations, sont exécutées par 16 modules VLIW en huit cycles d'horloge. Une alternance se produit, et chaque module dispose en réalité de deux cycles d'horloge pour exécuter l'ensemble de l'instruction, à condition qu'il commence à en exécuter une nouvelle en parallèle au deuxième cycle d'horloge. Cela aide probablement à exécuter rapidement une instruction VLIW comme a1 × a2 + b1 × b2 + c1 × c2 + d1 × d2, c'est-à-dire à exécuter huit de ces instructions en huit cycles d'horloge. (Formellement, il s'avère, un par battement.)

Nvidia ne semble pas avoir une telle technologie. Et en l'absence de VLIW, des performances élevées utilisant des instructions scalaires nécessitent une fréquence de fonctionnement élevée, ce qui augmente automatiquement la dissipation de chaleur et impose des exigences élevées au processus (pour faire fonctionner le circuit à une fréquence plus élevée).

L'inconvénient de Radeon en termes de GPU computing est sa grande aversion pour le branchement. Les GPU ne favorisent généralement pas le branchement en raison de la technologie d'exécution d'instructions décrite ci-dessus : à la fois avec un groupe de threads avec une adresse de programme. (Au fait, cette technique s'appelle SIMT : Single Instruction - Multiple Threads (une instruction - plusieurs threads), par analogie avec SIMD, où une instruction effectue une opération avec des données différentes.) ... Il est clair que si le programme n'est pas complètement vectoriel, alors plus la taille de la chaîne ou du front d'onde est grande, pire, car lorsque le chemin des fils adjacents diverge dans le programme, plus de groupes sont formés qui doivent être exécutés séquentiellement (sérialisés ). Disons que tous les threads sont dispersés, alors dans le cas d'une taille de chaîne de 32 threads, le programme s'exécutera 32 fois plus lentement. Et dans le cas de la taille 64, comme dans la Radeon, il est 64 fois plus lent.

C'est une manifestation notable, mais pas la seule, de « n'aime pas ». Dans les cartes vidéo Nvidia, chaque module fonctionnel, autrement appelé le noyau CUDA, possède une unité de traitement de branche spéciale. Et dans les cartes vidéo Radeon pour 16 modules de calcul, il n'y a que deux unités de contrôle de branche (elles sont dérivées du domaine des unités arithmétiques). Ainsi, même le simple traitement d'une instruction de branchement conditionnel, même si son résultat est le même pour tous les threads du front d'onde, prend du temps supplémentaire. Et la vitesse s'affaisse.

AMD produit également des processeurs. Ils pensent que pour les programmes avec beaucoup de branches, le CPU est encore meilleur, et le GPU est destiné aux programmes purement vectoriels.

Radeon offre donc moins d'options de programmation globales, mais offre un meilleur rapport qualité-prix dans de nombreux cas. En d'autres termes, il y a moins de programmes qui peuvent être efficacement (utilement) migrés du CPU vers la Radeon que de programmes qui peuvent s'exécuter efficacement sur Fermi. Mais d'un autre côté, ceux qui peuvent être transférés efficacement fonctionneront plus efficacement sur la Radeon à bien des égards.

API de calcul GPU

Les spécifications techniques des Radeon en elles-mêmes semblent séduisantes, même si cela ne vaut pas la peine d'idéaliser et d'absolutiser le GPU computing. Mais non moins important pour les performances est le logiciel requis pour le développement et l'exécution d'un programme GPU - des compilateurs à partir d'un langage et d'une exécution de haut niveau, c'est-à-dire un pilote qui interagit entre une partie d'un programme s'exécutant sur un processeur et le GPU lui-même. C'est encore plus important que dans le cas du CPU : le CPU n'a pas besoin de pilote pour gérer le transfert des données, et du point de vue du compilateur, le GPU est plus capricieux. Par exemple, le compilateur doit se contenter d'un nombre minimum de registres pour stocker les résultats intermédiaires des calculs, et aussi soigneusement les appels de fonction en ligne, encore une fois en utilisant un minimum de registres. Après tout, moins un thread utilise de registres, plus de threads peuvent être démarrés et plus le GPU est complètement chargé, masquant ainsi mieux le temps d'accès à la mémoire.

Et maintenant, le support logiciel pour les produits Radeon est toujours à la traîne par rapport au développement du matériel. (Contrairement à la situation avec Nvidia, où la sortie du matériel a été reportée, et le produit a été publié sous une forme tronquée.) Plus récemment, le compilateur OpenCL d'AMD avait un statut bêta, avec de nombreuses failles. Il a trop souvent généré du code erroné ou refusé de compiler le code à partir de la bonne source, ou il a lui-même donné une erreur de travail et s'est bloqué. Ce n'est qu'à la fin du printemps qu'il y a eu une sortie avec une haute performance. Il n'est pas non plus exempt d'erreurs, mais il y en a beaucoup moins et elles surviennent généralement dans des directions latérales lorsqu'elles essaient de programmer quelque chose à la limite de l'exactitude. Par exemple, ils fonctionnent avec le type uchar4, qui spécifie une variable à quatre composants de 4 octets. Ce type est dans les spécifications OpenCL, mais ce n'est pas la peine de travailler avec sur une Radeon, car les registres sont en 128 bits : les quatre mêmes composants, mais en 32 bits. Et une telle variable uchar4 occupera toujours un registre entier, seules des opérations supplémentaires d'emballage et d'accès aux composants d'octets individuels sont toujours nécessaires. Le compilateur ne devrait pas avoir d'erreurs, mais il n'y a pas de compilateurs sans défauts. Même le compilateur Intel après 11 versions a des erreurs de compilation. Les erreurs identifiées sont corrigées dans la prochaine version, qui sera publiée plus près de l'automne.

Mais il reste encore beaucoup de choses à régler. Par exemple, jusqu'à présent, le pilote GPU standard pour Radeon ne prend pas en charge le calcul GPU à l'aide d'OpenCL. L'utilisateur doit télécharger et installer un package personnalisé supplémentaire.

Mais le plus important est l'absence de bibliothèques de fonctions. Pour les nombres réels en double précision, il n'y a même pas de sinus, de cosinus et d'exposant. Eh bien, ce n'est pas nécessaire pour l'addition-multiplication matricielle, mais si vous voulez programmer quelque chose de plus complexe, vous devez écrire toutes les fonctions à partir de zéro. Ou attendez une nouvelle version du SDK. Prochainement ACML (AMD Core Math Library) pour la famille de GPU Evergreen avec prise en charge des fonctions matricielles de base.

Pour l'instant, selon l'auteur de l'article, l'utilisation de l'API Direct Compute 5.0 semble être réaliste pour programmer les cartes vidéo Radeon, compte tenu naturellement des limites : focus sur la plate-forme Windows 7 et Windows Vista. Microsoft a beaucoup d'expérience dans la création de compilateurs, et vous pouvez vous attendre à une version entièrement fonctionnelle très bientôt, Microsoft est directement intéressé par cela. Mais Direct Compute se concentre sur les besoins des applications interactives : calculer quelque chose et visualiser immédiatement le résultat - par exemple, l'écoulement d'un liquide sur une surface. Cela ne veut pas dire qu'il ne peut pas être utilisé simplement pour des calculs, mais ce n'est pas son objectif naturel. Par exemple, Microsoft ne prévoit pas d'ajouter des fonctions de bibliothèque à Direct Compute - juste celles qu'AMD ne possède pas pour le moment. C'est-à-dire que ce qui peut maintenant être calculé efficacement sur Radeon - certains programmes pas trop sophistiqués - peut être implémenté sur Direct Compute, qui est beaucoup plus simple qu'OpenCL et devrait être plus stable. De plus, il est entièrement portable, il fonctionnera à la fois sur Nvidia et AMD, vous ne devez donc compiler le programme qu'une seule fois, alors que les implémentations OpenCL SDK de Nvidia et AMD ne sont pas tout à fait compatibles. (En ce sens que si vous développez un programme OpenCL sur un système AMD à l'aide du SDK AMD OpenCL, il se peut que cela ne se passe pas aussi facilement sur Nvidia. Vous devrez peut-être compiler le même texte à l'aide du SDK Nvidia. Et, bien sûr, vice versa .)

Ensuite, il y a beaucoup de fonctionnalités redondantes dans OpenCL, car OpenCL est conçu comme un langage de programmation universel et une API pour un large éventail de systèmes. Et GPU, CPU et Cell. Ainsi, au cas où vous auriez juste besoin d'écrire un programme pour un système utilisateur typique (processeur plus carte vidéo), OpenCL ne semble pas, pour ainsi dire, "très productif". Chaque fonction a dix paramètres, et neuf d'entre eux doivent être définis sur 0. Et pour définir chaque paramètre, vous devez appeler une fonction spéciale qui a également des paramètres.

Et l'avantage actuel le plus important de Direct Compute est que l'utilisateur n'a pas besoin d'installer un package spécial : tout ce qui est nécessaire est déjà dans DirectX 11.

Problèmes de développement de l'informatique GPU

Si nous prenons le domaine des ordinateurs personnels, alors la situation est la suivante : il n'y a pas beaucoup de tâches qui nécessitent beaucoup de puissance de calcul et qui manquent grandement d'un processeur dual-core classique. Comme de la mer, de gros monstres voraces mais maladroits ont rampé hors de la mer, mais sur terre, il n'y a presque rien à manger. Et les habitations primordiales de la surface de la terre diminuent de taille, apprenant à consommer moins, comme c'est toujours le cas en cas de pénurie de ressources naturelles. S'il y avait maintenant le même besoin de performances qu'il y a 10-15 ans, le GPU computing serait accepté avec brio. C'est ainsi que les problèmes de compatibilité et la relative complexité de la programmation GPU viennent au premier plan. Il vaut mieux écrire un programme qui fonctionne sur tous les systèmes qu'un programme qui est rapide mais ne fonctionne que sur le GPU.

La perspective GPU est légèrement meilleure en termes d'utilisation dans les applications professionnelles et le secteur des stations de travail, car il y a plus de demande de performance. Il existe des plugins pour les éditeurs 3D compatibles GPU : par exemple, pour le rendu à l'aide du lancer de rayons - à ne pas confondre avec le rendu GPU classique ! Quelque chose surgit pour les éditeurs 2D et les éditeurs de présentation, avec la vitesse de création d'effets complexes. Les programmes de traitement vidéo acquièrent également progressivement la prise en charge du GPU. Les tâches ci-dessus, compte tenu de leur nature parallèle, s'intègrent bien avec l'architecture GPU, mais maintenant une très grande base de code a été créée, déboguée, optimisée pour toutes les capacités du CPU, il faudra donc du temps pour que de bonnes implémentations GPU apparaître.

Ce segment révèle également des faiblesses du GPU telles que la quantité limitée de mémoire vidéo - environ 1 Go pour les GPU conventionnels. L'un des principaux facteurs qui ralentit les performances des programmes GPU est la nécessité d'échanger des données entre le CPU et le GPU sur un bus lent, et en raison de la quantité limitée de mémoire, davantage de données doivent être transférées. Et ici, le concept d'AMD consistant à combiner GPU et CPU dans un seul module semble prometteur : vous pouvez sacrifier la bande passante élevée de la mémoire graphique pour un accès facile et simple à la mémoire partagée, de plus, avec une latence plus faible. Cette bande passante mémoire élevée de la mémoire vidéo DDR5 actuelle est beaucoup plus demandée par les programmes graphiques directs que par la plupart des programmes de calcul GPU. En général, la mémoire partagée du GPU et du CPU élargira simplement considérablement la portée du GPU, permettra d'utiliser ses capacités de calcul dans de petites sous-tâches de programmes.

Et surtout, les GPU sont demandés dans le domaine du calcul scientifique. Plusieurs supercalculateurs basés sur GPU ont déjà été construits, qui affichent des résultats très élevés dans le test des opérations matricielles. Les problèmes scientifiques sont tellement variés et nombreux qu'il y en a toujours beaucoup qui s'intègrent parfaitement à l'architecture GPU, pour laquelle l'utilisation du GPU permet d'obtenir facilement des performances élevées.

Si vous en choisissez une parmi toutes les tâches des ordinateurs modernes, ce sera l'infographie - une image du monde dans lequel nous vivons. Et l'architecture optimale à cet effet ne peut pas être mauvaise. Il s'agit d'une tâche si importante et fondamentale que le matériel spécialement conçu à cet effet doit être polyvalent et optimal pour diverses tâches. De plus, les cartes vidéo évoluent avec succès.

Aujourd'hui, des nouvelles sur l'utilisation des GPU pour l'informatique générale peuvent être entendues à chaque coin de rue. Des mots tels que CUDA, Stream et OpenCL sont devenus presque les plus cités sur l'Internet informatique en seulement deux ans. Cependant, tout le monde ne sait pas ce que signifient ces mots et ce que signifient les technologies qui les sous-tendent. Et pour les utilisateurs de Linux, habitués à "être en fuite", et en général, tout cela est vu comme une forêt sombre.

Naissance du GPGPU

Nous sommes tous habitués à penser que le seul composant d'un ordinateur capable d'exécuter n'importe quel code qui lui est ordonné est le processeur central. Pendant longtemps, presque tous les PC grand public étaient équipés d'un seul processeur qui effectuait tous les calculs imaginables, y compris le code du système d'exploitation, tous nos logiciels et virus.

Plus tard, des processeurs multicœurs et des systèmes multiprocesseurs sont apparus, dans lesquels il y avait plusieurs de ces composants. Cela a permis aux machines d'effectuer plusieurs tâches en même temps, et les performances globales (théoriques) du système ont augmenté exactement autant que le nombre de cœurs installés dans la machine. Cependant, il s'est avéré que la fabrication et la conception de processeurs multicœurs sont trop difficiles et coûteuses.

Chaque cœur devait abriter un processeur à part entière d'une architecture x86 complexe et déroutante, avec son propre cache (plutôt volumineux), son pipeline d'instructions, ses blocs SSE, de nombreux blocs effectuant des optimisations, etc. etc. Par conséquent, le processus d'augmentation du nombre de cœurs s'est considérablement ralenti et les blouses universitaires blanches, pour lesquelles deux ou quatre cœurs n'étaient clairement pas suffisants, ont trouvé un moyen d'utiliser d'autres puissances de calcul pour leurs calculs scientifiques, qui étaient en abondance sur la vidéo. carte (en conséquence, même l'outil BrookGPU est apparu, émulant un processeur supplémentaire à l'aide d'appels de fonction DirectX et OpenGL).

Les processeurs graphiques, dépourvus de bon nombre des défauts d'un processeur central, se sont avérés être une excellente machine à calculer très rapide, et très vite les fabricants de GPU eux-mêmes ont commencé à examiner de près les développements des scientifiques (et nVidia a embauché la plupart des chercheurs au travail). Le résultat est la technologie CUDA de nVidia, qui définit une interface permettant de transférer le calcul d'algorithmes complexes sur les épaules du GPU sans aucune béquille. Il a ensuite été suivi par ATi (AMD) avec sa propre version de la technologie appelée Close to Metal (maintenant Stream), et très vite, il y avait une version standard d'Apple appelée OpenCL.

Le GPU est-il notre tout ?

Malgré tous ses avantages, la technique GPGPU présente plusieurs problèmes. La première est une portée très étroite. Les GPU ont devancé le processeur central en termes d'augmentation de la puissance de calcul et du nombre total de cœurs (les cartes vidéo comportent une unité de calcul composée de plus d'une centaine de cœurs), mais une telle densité est obtenue grâce à la simplification maximale de la conception de la puce elle-même.

Essentiellement, la tâche principale du GPU est réduite à des calculs mathématiques utilisant des algorithmes simples qui reçoivent en entrée de très petites quantités de données prévisibles. Pour cette raison, les cœurs GPU ont une conception très simple, des tailles de cache limitées et un ensemble d'instructions modeste, ce qui se traduit finalement par un faible coût de production et la possibilité d'un placement très dense sur une puce. Les GPU sont comme une usine chinoise avec des milliers de travailleurs. Ils font des choses simples assez bien (et surtout - rapidement et à moindre coût), mais si vous leur confiez l'assemblage de l'avion, le résultat sera un deltaplane maximal.

Par conséquent, la première limitation du GPU est sa focalisation sur les calculs mathématiques rapides, ce qui limite le champ d'utilisation des GPU pour aider dans le travail des applications multimédias, ainsi que tous les programmes impliqués dans le traitement de données complexes (par exemple, les archiveurs ou le cryptage systèmes, ainsi que des logiciels pour la microscopie à fluorescence, la dynamique moléculaire, l'électrostatique et d'autres choses de peu d'intérêt pour les utilisateurs de Linux).

Le deuxième problème avec GPGPU est que tous les algorithmes ne peuvent pas être adaptés pour une exécution sur le GPU. Les cœurs GPU séparés sont assez lents et leur puissance n'est apparente que lorsqu'ils travaillent ensemble. Cela signifie que l'algorithme sera aussi efficace que le programmeur pourra le paralléliser efficacement. Dans la plupart des cas, seul un bon mathématicien peut gérer un tel travail, et il y en a très peu parmi les développeurs de logiciels.

Et troisièmement : les GPU fonctionnent avec la mémoire installée sur la carte vidéo elle-même, de sorte qu'à chaque fois que le GPU est activé, il y aura deux opérations de copie supplémentaires : les données d'entrée de la RAM de l'application elle-même et les données de sortie de la GRAM vers la mémoire de l'application . Comme vous pouvez le deviner, cela peut annuler tout le gain en temps d'exécution de l'application (comme c'est le cas avec l'outil FlacCL, que nous verrons plus tard).

Mais ce n'est pas tout. Malgré l'existence d'une norme généralement acceptée face à OpenCL, de nombreux programmeurs préfèrent toujours utiliser des implémentations spécifiques au fournisseur de la technique GPGPU. CUDA s'est avéré être particulièrement populaire, qui, bien qu'il fournisse une interface de programmation plus flexible (d'ailleurs, OpenCL dans les pilotes nVidia est implémenté au-dessus de CUDA), mais lie étroitement l'application aux cartes vidéo d'un fabricant.

Noyau KGPU ou Linux, accéléré par GPU

Des chercheurs de l'Université de l'Utah ont développé un système KGPU qui permet à certaines fonctions du noyau Linux de s'exécuter sur un GPU à l'aide du framework CUDA. Pour effectuer cette tâche, un noyau Linux modifié et un démon spécial sont utilisés qui s'exécutent dans l'espace utilisateur, écoute les requêtes du noyau et les envoie au pilote de la carte vidéo à l'aide de la bibliothèque CUDA. Fait intéressant, malgré la surcharge importante qu'une telle architecture crée, les auteurs de KGPU ont réussi à créer une implémentation de l'algorithme AES, qui multiplie par 6 la vitesse de cryptage du système de fichiers eCryptfs.

Qu'y a-t-il maintenant?

En raison de sa jeunesse, et aussi en raison des problèmes décrits ci-dessus, le GPGPU n'est pas devenu une technologie vraiment répandue, cependant, des logiciels utiles qui utilisent ses capacités existent (bien qu'en quantité limitée). L'un des premiers à apparaître était des crackers de divers hachages, dont les algorithmes sont très faciles à paralléliser.

Aussi, des applications multimédias sont nées, par exemple, l'encodeur FlacCL, qui permet de transcoder une piste audio au format FLAC. Certaines applications préexistantes ont également acquis le support GPGPU, dont la plus notable est ImageMagick, qui peut désormais transférer une partie de son travail vers le GPU à l'aide d'OpenCL. Il existe également des projets de traduction d'archiveurs de données et d'autres systèmes de compression d'informations vers CUDA / OpenCL (ils n'aiment pas les unixoïdes d'ATI). Nous examinerons le plus intéressant de ces projets dans les sections suivantes de l'article, mais pour l'instant, nous essaierons de déterminer ce dont nous avons besoin pour que tout cela démarre et fonctionne de manière stable.

Les GPU ont depuis longtemps dépassé les processeurs x86 en termes de performances

· Deuxièmement, les derniers pilotes propriétaires pour la carte vidéo doivent être installés dans le système, ils prendront en charge à la fois les technologies GPGPU natives de la carte et OpenCL ouvert.

· Et troisièmement, puisque les constructeurs de distribution n'ont pas encore commencé à distribuer des packages d'applications avec le support GPGPU, nous devrons construire des applications nous-mêmes, et pour cela nous avons besoin des SDK officiels des fabricants : CUDA Toolkit ou ATI Stream SDK. Ils contiennent les fichiers d'en-tête et les bibliothèques nécessaires à la création d'applications.

Installation de la boîte à outils CUDA

Suivez le lien ci-dessus et téléchargez la boîte à outils CUDA pour Linux (vous pouvez choisir parmi plusieurs versions, pour les distributions Fedora, RHEL, Ubuntu et SUSE, il existe des versions pour les architectures x86 et x86_64). De plus, vous devez y télécharger les kits de pilotes de développement (pilotes de développement pour Linux, ils sont les premiers de la liste).

Lancez le programme d'installation du SDK :

$ sudo sh cudatoolkit_4.0.17_linux_64_ubuntu10.10.run

Une fois l'installation terminée, nous procédons à l'installation des pilotes. Pour ce faire, nous arrêtons le serveur X :

# sudo /etc/init.d/gdm stop

Ouverture de la console et lancez le programme d'installation du pilote :

$ sudo sh devdriver_4.0_linux_64_270.41.19.run

Une fois l'installation terminée, démarrez le X :

Pour permettre aux applications de fonctionner avec CUDA / OpenCL, nous écrivons le chemin du répertoire avec les bibliothèques CUDA dans la variable LD_LIBRARY_PATH :

$ export LD_LIBRARY_PATH = / usr / local / cuda / lib64

Ou, si vous avez installé la version 32 bits :

$ export LD_LIBRARY_PATH = / usr / local / cuda / lib32

Il est également nécessaire d'enregistrer le chemin des fichiers d'en-tête CUDA pour que le compilateur les retrouve au stade de la construction de l'application :

$ export C_INCLUDE_PATH = / usr / local / cuda / include

Ça y est, vous pouvez maintenant commencer à créer le logiciel CUDA / OpenCL.

Installer le SDK ATI Stream

Stream SDK ne nécessite pas d'installation, donc l'archive AMD téléchargée peut être simplement décompressée dans n'importe quel répertoire (/ opt est le meilleur choix) et écrire le chemin d'accès dans la même variable LD_LIBRARY_PATH :

$ wget http://goo.gl/CNCNo

$ sudo tar -xzf ~ / AMD-APP-SDK-v2.4-lnx64.tgz -C / opt

$ export LD_LIBRARY_PATH = / opt / AMD-APP-SDK-v2.4-lnx64 / lib / x86_64 /

$ export C_INCLUDE_PATH = / opt / AMD-APP-SDK-v2.4-lnx64 / include /

Comme pour la boîte à outils CUDA, x86_64 doit être remplacé par x86 sur les systèmes 32 bits. Allez maintenant dans le répertoire racine et décompressez l'archive icd-registration.tgz (c'est une sorte de clé de licence gratuite) :

$ sudo tar -xzf /opt/AMD-APP-SDK-v2.4-lnx64/icd-registration.tgz - AVEC /

Nous vérifions l'exactitude de l'installation / du fonctionnement du package à l'aide de l'outil clinfo :

$ /opt/AMD-APP-SDK-v2.4-lnx64/bin/x86_64/clinfo

ImageMagick et OpenCL

Le support d'OpenCL est apparu dans ImageMagick depuis longtemps, mais il n'est activé par défaut dans aucune distribution. Par conséquent, nous devrons créer nous-mêmes la messagerie instantanée à partir de la source. Il n'y a rien de compliqué à cela, tout ce dont vous avez besoin est déjà dans le SDK, donc l'assemblage ne nécessite pas l'installation de bibliothèques supplémentaires de nVidia ou d'AMD. Alors, téléchargez/décompressez l'archive avec les sources :

$ wget http://goo.gl/F6VYV

$ tar -xjf ImageMagick-6.7.0-0.tar.bz2

$ cd ImageMagick-6.7.0-0

$ sudo apt-get install build-essential

Nous lançons le configurateur et vérifions sa sortie pour la prise en charge d'OpenCL :

$ LDFLAGS = -L $ LD_LIBRARY_PATH ./configuration | grep -e cl.h -e OpenCL

La sortie correcte de la commande devrait ressembler à ceci :

vérification de l'utilisabilité CL/cl.h... oui

vérification présence CL / cl.h ... oui

vérification de CL / cl.h ... oui

vérification de la convivialité d'OpenCL / cl.h ... non

vérification de la présence OpenCL / cl.h ... non

vérifier OpenCL / cl.h ... non

vérification de la bibliothèque OpenCL ... -lOpenCL

Les trois premières lignes ou la seconde (ou les deux) doivent être marquées du mot « oui ». Si ce n'est pas le cas, il est fort probable que la variable C_INCLUDE_PATH a été mal initialisée. Si la dernière ligne est marquée du mot "non", alors le problème est dans la variable LD_LIBRARY_PATH. Si tout va bien, lancez le processus de compilation/installation :

$ sudo make install clean

Vérifiez qu'ImageMagick a bien été compilé avec le support OpenCL :

$/usr/local/bin/convert -version | grep Caractéristiques

Fonctionnalités : OpenMP OpenCL

Maintenant, mesurons le gain de vitesse qui en résulte. Les développeurs d'ImageMagick recommandent d'utiliser le filtre convolve pour cela :

$ time / usr / bin / convert image.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" image2.jpg

$ time / usr / local / bin / convert image.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" image2.jpg

Certaines autres opérations, telles que le redimensionnement, devraient désormais également fonctionner beaucoup plus rapidement, mais vous ne devez pas espérer qu'ImageMagick commencera à traiter les graphiques à une vitesse vertigineuse. Jusqu'à présent, une très petite partie du package a été optimisée avec OpenCL.

FlacCL (Flacuda)

FlacCL est un encodeur audio FLAC qui exploite les capacités OpenCL. Il est inclus dans le package CUETools pour Windows, mais grâce à mono, il peut également être utilisé sous Linux. Pour obtenir l'archive avec l'encodeur, exécutez la commande suivante :

$ mkdir flaccl && cd flaccl

$ wget www.cuetools.net/install/flaccl03.rar

$ sudo apt-get install unrar mono

$ unrar x fl accl03.rar

Pour que le programme puisse trouver la bibliothèque OpenCL, nous faisons un lien symbolique :

$ ln -s $ LD_LIBRARY_PATH / libOpenCL.so libopencl.so

Commençons maintenant l'encodeur :

$ mono CUETools.FLACCL.cmd.exe music.wav

Si le message d'erreur "Erreur : la taille de compilation demandée est supérieure à la taille de groupe de travail requise de 32" apparaît à l'écran, cela signifie que notre carte vidéo est trop faible dans le système et que le nombre de cœurs impliqués doit être réduit au nombre spécifié. nombre utilisant le « - group-size XX », où XX est le nombre de cœurs requis.

Je dois dire tout de suite qu'en raison du temps d'initialisation long d'OpenCL, un gain notable ne peut être obtenu que sur des pistes suffisamment longues. FlacCL traite les fichiers audio courts à presque la même vitesse que sa version traditionnelle.

oclHashcat ou force brute rapide

Comme je l'ai dit, les développeurs de divers crackers et systèmes de mots de passe par force brute ont été parmi les premiers à ajouter la prise en charge de GPGPU à leurs produits. Pour eux, la nouvelle technologie est devenue un véritable Saint Graal, qui a permis de transférer facilement de la nature du code facilement parallélisable aux épaules de processeurs GPU rapides. Par conséquent, il n'est pas surprenant qu'il existe maintenant des dizaines de mises en œuvre très différentes de tels programmes. Mais dans cet article, je ne parlerai que de l'un d'entre eux - oclHashcat.

oclHashcat est un disjoncteur qui peut forcer les mots de passe par leur hachage à une vitesse extrêmement élevée, tout en exploitant la puissance du GPU à l'aide d'OpenCL. Si vous en croyez les mesures publiées sur le site Web du projet, la vitesse des mots de passe de force brute MD5 sur nVidia GTX580 peut atteindre 15 800 millions de combinaisons par seconde, grâce auxquelles oclHashcat est capable de trouver un mot de passe moyen de 8 caractères en 9 minutes.

Le programme prend en charge OpenCL et CUDA, les algorithmes MD5, md5 ($ pass. $ Salt), md5 (md5 ($ pass)), vBulletin< v3.8.5, SHA1, sha1($pass.$salt), хэши MySQL, MD4, NTLM, Domain Cached Credentials, SHA256, поддерживает распределенный подбор паролей с задействованием мощности нескольких машин.

$ 7z x oclHashcat-0.25.7z

$ cd oclHashcat-0,25

Et exécutez le programme (utilisons une liste de hachage d'essai et un dictionnaire d'essai) :

$ ./oclHashcat64.bin exemple.hash? l? l? l? l exemple.dict

oclHashcat ouvrira le texte de l'accord d'utilisation, qui doit être accepté en tapant « OUI ». Après cela, le processus d'énumération commencera, dont la progression peut être consultée en cliquant sur ... Pour suspendre le processus, appuyez sur

Pour reprendre - ... Vous pouvez également utiliser la force brute (par exemple, de aaaaaaaa à zzzzzzzz) :

$ ./oclHashcat64.bin hash.txt? l? l? l? l? l? l? l? l

Et diverses modifications du dictionnaire et de la méthode brute force, ainsi que leurs combinaisons (vous pouvez lire à ce sujet dans le fichier docs/examples.txt). Dans mon cas, la vitesse de recherche de l'ensemble du dictionnaire était de 11 minutes, tandis que la recherche avant (de aaaaaaaa à zzzzzzzz) a duré environ 40 minutes. La vitesse moyenne du GPU (puce RV710) était de 88,3 millions/s.

conclusions

Malgré de nombreuses limitations différentes et la complexité du développement logiciel, GPGPU est l'avenir des ordinateurs de bureau hautes performances. Mais le plus important est que vous puissiez utiliser les capacités de cette technologie dès maintenant, et cela s'applique non seulement aux machines Windows, mais aussi à Linux.


En parlant de calcul parallèle sur un GPU, nous devons nous rappeler à quelle époque nous vivons, aujourd'hui, c'est une époque où tout dans le monde est tellement accéléré que vous et moi perdons la notion du temps, sans remarquer à quel point il se précipite. Tout ce que nous faisons est associé à une grande précision et rapidité de traitement de l'information. Dans de telles conditions, nous avons certainement besoin d'outils pour traiter toutes les informations dont nous disposons et les convertir en données. De plus, en parlant de telles tâches, nous devons nous rappeler que ces tâches sont nécessaires non seulement pour les grandes organisations ou les méga-entreprises, les utilisateurs ordinaires qui résolvent leurs tâches quotidiennes liées aux hautes technologies à la maison sur des ordinateurs personnels doivent désormais également résoudre de tels problèmes ! L'apparition de NVIDIA CUDA n'était pas surprenante, mais plutôt justifiée, car bientôt il faudra traiter des tâches beaucoup plus chronophages sur un PC qu'auparavant. Le travail, qui prenait auparavant beaucoup de temps, va maintenant prendre quelques minutes, et par conséquent cela affectera l'image globale du monde entier !

Qu'est-ce que le GPU computing

Le calcul sur un GPU est l'utilisation d'un GPU pour calculer des tâches techniques, scientifiques et quotidiennes. Le calcul sur GPU implique l'utilisation d'un CPU et d'un GPU avec un échantillonnage hétérogène entre eux, à savoir : le CPU prend en charge la partie séquentielle des programmes, tandis que les tâches de calcul chronophages sont laissées au GPU. De ce fait, une parallélisation des tâches se produit, ce qui entraîne une accélération du traitement de l'information et réduit le temps d'exécution, le système devient plus productif et peut traiter simultanément plus de tâches qu'auparavant. Cependant, pour atteindre un tel succès, le support matériel seul ne suffit pas, dans ce cas, le support logiciel est également nécessaire pour que l'application puisse transférer les calculs les plus chronophages vers le GPU.

Qu'est-ce que CUDA

CUDA est une technologie de programmation C simplifiée pour les algorithmes qui s'exécutent sur les GPU GeForce de 8e génération et ultérieures, ainsi que sur les cartes Quadro et Tesla correspondantes de NVIDIA. CUDA permet d'inclure des fonctions spéciales dans le texte C d'un programme. Ces fonctions sont écrites dans un langage de programmation C simplifié et exécutées sur le GPU. La version initiale du SDK CUDA a été publiée le 15 février 2007. Pour traduire avec succès le code dans ce langage, le SDK CUDA inclut le propre compilateur C en ligne de commande de NVIDIA, nvcc. Le compilateur nvcc est basé sur le compilateur open-source Open64 et est conçu pour traduire le code hôte (principal, code de contrôle) et le code de périphérique (code matériel) (fichiers avec l'extension .cu) en fichiers objets adaptés à la construction du programme final ou une bibliothèque dans n'importe quel environnement de programmation tel que Microsoft Visual Studio.

Capacités technologiques

  1. Un langage C standard pour le développement parallèle d'applications GPU.
  2. Bibliothèques d'analyse numérique prêtes à l'emploi pour les transformées de Fourier rapides et le logiciel d'algèbre linéaire de base.
  3. Pilote CUDA dédié pour des calculs de transfert de données rapides entre GPU et CPU.
  4. Possibilité d'interaction du pilote CUDA avec les pilotes graphiques OpenGL et DirectX.
  5. Prend en charge les systèmes d'exploitation Linux 32/64 bits, Windows XP 32/64 bits et MacOS.

Avantages technologiques

  1. L'interface de programmation d'applications CUDA (API CUDA) est basée sur le langage de programmation C standard avec certaines restrictions. Cela rend l'apprentissage de l'architecture CUDA plus facile et plus fluide.
  2. La mémoire partagée (mémoire partagée) d'une taille de 16 Ko peut être utilisée pour un cache organisé par l'utilisateur avec une bande passante plus large que lors de l'échantillonnage à partir de textures ordinaires.
  3. Transactions plus efficaces entre la mémoire CPU et la mémoire vidéo.
  4. Prise en charge matérielle complète des opérations entières et au niveau du bit.

Exemple d'application technologique

cRark

La partie la plus longue de ce programme est la teinture. Le programme a une interface console, mais grâce aux instructions jointes au programme lui-même, vous pouvez l'utiliser. Vous trouverez ci-dessous un bref guide de configuration du programme. Nous allons vérifier les performances du programme et le comparer avec un autre programme similaire qui n'utilise pas NVIDIA CUDA, dans ce cas il s'agit du programme bien connu "Advanced Archive Password Recovery".

À partir de l'archive cRark téléchargée, nous n'avons besoin que de trois fichiers : crark.exe, crark-hp.exe et password.def. Сrark.exe est un utilitaire de console pour déchiffrer les mots de passe RAR 3.0 sans fichiers cryptés à l'intérieur de l'archive (c'est-à-dire que lors de l'ouverture de l'archive, nous voyons les noms, mais nous ne pouvons pas décompresser l'archive sans mot de passe).

Сrark-hp.exe est un utilitaire de console pour déchiffrer les mots de passe RAR 3.0 avec cryptage de l'archive entière (c'est-à-dire, lors de l'ouverture de l'archive, nous ne voyons ni le nom ni les archives elles-mêmes, et nous ne pouvons pas décompresser l'archive sans mot de passe) .

Password.def est tout fichier texte renommé avec très peu de contenu (par exemple : 1ère ligne : ## 2ème ligne :? *, dans ce cas le mot de passe sera craqué en utilisant tous les caractères). Password.def est le gestionnaire de programme cRark. Le fichier contient les règles de craquage du mot de passe (ou la zone de caractères que crark.exe utilisera dans son travail). Plus de détails sur les possibilités de choix de ces caractères sont écrits dans le fichier texte obtenu en ouvrant le fichier téléchargé de l'auteur du programme cRark : russian.def.

Préparation

Je dois dire tout de suite que le programme ne fonctionne que si votre carte vidéo est basée sur un GPU prenant en charge le niveau d'accélération CUDA 1.1. Ainsi, une série de cartes vidéo basées sur la puce G80, telles que la GeForce 8800 GTX, sont abandonnées, car elles prennent en charge matériellement l'accélération CUDA 1.0. Le programme sélectionne en utilisant CUDA uniquement des mots de passe pour les archives RAR des versions 3.0+. Tous les logiciels liés à CUDA doivent être installés, à savoir :

Nous créons n'importe quel dossier n'importe où (par exemple, sur le lecteur C :) et l'appelons n'importe quel nom, par exemple "3.2". Placez-y les fichiers : crark.exe, crark-hp.exe et password.def et une archive RAR protégée par mot de passe/cryptée.

Ensuite, vous devez démarrer la console de ligne de commande Windows et accéder au dossier créé. Sous Windows Vista et 7, ouvrez le menu Démarrer et saisissez "cmd.exe" dans le champ de recherche ; sous Windows XP, à partir du menu Démarrer, ouvrez d'abord la boîte de dialogue Exécuter et saisissez "cmd.exe" dedans. Après avoir ouvert la console, saisissez une commande de la forme : cd C:\dossier\, cd C:\ 3.2 dans ce cas.

Nous tapons deux lignes dans un éditeur de texte (vous pouvez également enregistrer le texte en tant que fichier .bat dans le dossier cRark) pour forcer le mot de passe pour une archive RAR protégée par mot de passe avec des fichiers non cryptés :

Écho off;
cmd / K crark (nom de l'archive) .rar

pour forcer brutalement un mot de passe pour une archive RAR protégée par mot de passe et cryptée :

Écho off;
cmd / K crark-hp (nom de l'archive) .rar

Copiez 2 lignes du fichier texte sur la console et appuyez sur Entrée (ou exécutez le fichier .bat).

résultats

Le processus de décryptage est illustré dans la figure :

Le taux d'attaque par force brute sur cRark à l'aide de CUDA était de 1625 mots de passe / seconde. En une minute et trente-six secondes, un mot de passe à 3 caractères a été trouvé : "q) $". A titre de comparaison : la vitesse de recherche dans Advanced Archive Password Recovery sur mon processeur dual-core Athlon 3000+ est au maximum de 50 mots de passe/seconde, et la recherche devrait prendre 5 heures. C'est-à-dire que la sélection par force brute dans cRark d'une archive RAR à l'aide d'une carte vidéo GeForce 9800 GTX + est 30 fois plus rapide que sur un processeur.

Pour ceux qui ont un processeur Intel, une bonne carte mère avec une fréquence de bus système élevée (FSB 1600 MHz), le taux de CPU et la force brute seront plus élevés. Et si vous disposez d'un processeur quadricœur et d'une paire de cartes vidéo du niveau de GeForce 280 GTX, la vitesse de forçage brutal des mots de passe est considérablement accélérée. En résumant les résultats de l'exemple, je dois dire que ce problème a été résolu en utilisant la technologie CUDA en seulement 2 minutes au lieu de 5 heures, ce qui indique un fort potentiel pour cette technologie !

conclusions

Après avoir passé en revue aujourd'hui la technologie de calcul parallèle CUDA, nous avons clairement vu la puissance et l'énorme potentiel de développement de cette technologie à l'aide de l'exemple d'un programme de récupération d'un mot de passe pour les archives RAR. Je dois dire sur les perspectives de cette technologie, cette technologie trouvera certainement une place dans la vie de chaque personne qui décidera de l'utiliser, qu'il s'agisse de problèmes scientifiques, ou de problèmes liés au traitement vidéo, ou encore de problèmes économiques qui nécessitent une intervention rapide calcul précis, tout cela conduira à l'inévitable augmentation de la productivité du travail qui ne peut être négligée. Aujourd'hui, la combinaison de mots « superordinateur domestique » commence déjà à entrer dans le lexique ; il est absolument clair que pour traduire un tel objet dans la réalité, chaque foyer dispose déjà d'un outil appelé CUDA. Depuis la sortie des cartes basées sur la puce G80 (2006), un grand nombre d'accélérateurs NVIDIA ont été publiés qui prennent en charge la technologie CUDA, qui est capable de faire des rêves de superordinateurs dans chaque foyer une réalité. En faisant la promotion de la technologie CUDA, NVIDIA renforce sa crédibilité aux yeux des clients en fournissant des capacités supplémentaires à leurs équipements, que beaucoup ont déjà achetés. Il ne reste plus qu'à croire que dans un futur proche CUDA se développera très rapidement et permettra aux utilisateurs de profiter pleinement de toutes les possibilités du calcul parallèle sur le GPU.

Vous avez aimé l'article ? A partager entre amis :