Un bon point de départ est le excellent livre The Science of Programming Matrix Computations par Robert A. van de Geijn et Enrique S. Quintana-Ortí. Ils proposent une version téléchargeable gratuitement.
BLAS est divisé en trois niveaux :
-
Le niveau 1 définit un ensemble de fonctions d'algèbre linéaire qui opèrent uniquement sur des vecteurs. Ces fonctions bénéficient de la vectorisation (par exemple à partir de l'utilisation de SIMD tels que SSE).
-
Les fonctions du niveau 2 sont des opérations matrice-vecteur, par exemple un produit matrice-vecteur. Ces fonctions pourraient être mises en œuvre en termes de fonctions de niveau 1. Cependant, vous pouvez améliorer les performances de ces fonctions si vous pouvez fournir une mise en œuvre dédiée qui exploite une architecture multiprocesseur avec mémoire partagée.
-
Les fonctions de niveau 3 sont des opérations comme le produit matrice-matrice. Encore une fois, vous pourriez les implémenter en termes de fonctions de niveau 2. Mais les fonctions de niveau 3 effectuent des opérations O(N^3) sur des données O(N^2). Donc, si votre plate-forme dispose d'une hiérarchie de cache, vous pouvez améliorer les performances si vous fournissez une mise en œuvre dédiée qui est optimisée pour le cache/amie du cache. Cela est bien décrit dans le livre. L'augmentation principale des fonctions de niveau 3 provient de l'optimisation du cache. Cette amélioration dépasse de loin la deuxième augmentation obtenue grâce à la parallélisme et aux autres optimisations matérielles.
Soit dit en passant, la plupart (voire toutes) des implémentations BLAS haute performance ne sont PAS faites en Fortran. ATLAS est implémenté en C. GotoBLAS/OpenBLAS est implémenté en C et ses parties critiques en termes de performances en assembleur. Seule la mise en œuvre de référence de BLAS est faite en Fortran. Cependant, toutes ces implémentations BLAS fournissent une interface Fortran permettant de lier avec LAPACK (LAPACK tire toutes ses performances de BLAS).
Les compilateurs optimisés jouent un rôle mineur à cet égard (et pour GotoBLAS/OpenBLAS, le compilateur n'est pas du tout important).
À mon avis, aucune implémentation BLAS n'utilise des algorithmes comme l'algorithme de Coppersmith–Winograd ou l'algorithme de Strassen. Les raisons probables sont :
- Peut-être qu'il n'est pas possible de fournir une mise en œuvre optimisée pour le cache de ces algorithmes (c'est-à-dire que vous perdriez plus que vous ne gagneriez)
- Ces algorithmes ne sont pas numériquement stables. Comme BLAS est le noyau computationnel de LAPACK, cela ne peut pas être accepté.
- Bien que ces algorithmes aient une belle complexité temporelle sur papier, la notation Big O cache une grande constante, donc cela ne devient viable que pour des matrices extrêmement grandes.
Modifier/Mettre à jour :
Les nouveaux et révolutionnaires documents sur ce sujet sont les documents BLIS. Ils sont exceptionnellement bien écrits. Pour mon cours "Bases Logicielles pour le Calcul Haute Performance", j'ai implémenté le produit matrice-matrice suivant leur document. En fait, j'ai implémenté plusieurs variantes du produit matrice-matrice. La variante la plus simple est entièrement écrite en C pur et comporte moins de 450 lignes de code. Toutes les autres variantes ne font qu'optimiser les boucles
for (l=0; l
`
Les performances globales du produit matrice-matrice dépendent seulement de ces boucles. Environ 99,9% du temps est passé ici. Dans les autres variantes, j'ai utilisé des fonctions intrinsèques et du code assembleur pour améliorer les performances. Vous pouvez voir le tutoriel sur toutes les variantes ici :
ulmBLAS : Tutoriel sur GEMM (Produit Matrice-Matrice)
Avec les documents BLIS, il devient assez facile de comprendre comment des bibliothèques comme Intel MKL peuvent atteindre de telles performances. Et pourquoi cela n'a pas d'importance si vous utilisez un stockage par ligne ou par colonne !
Les derniers benchmarks se trouvent ici (nous avons appelé notre projet ulmBLAS) :
Benchmarks pour ulmBLAS, BLIS, MKL, openBLAS et Eigen
Autre modification/Mise à jour :
J'ai également écrit quelques tutoriels sur la façon dont BLAS est utilisée pour résoudre des problèmes d'algèbre linéaire numérique comme la résolution d'un système d'équations linéaires :
Factorisation LU à Haute Performance
(Cette factorisation LU est par exemple utilisée par Matlab pour résoudre un système d'équations linéaires.)
J'espère trouver le temps pour étendre le tutoriel afin de décrire et de montrer comment réaliser une implémentation parallèle hautement évolutive de la factorisation LU comme dans PLASMA.
D'accord, voici : Coder une Factorisation LU Parallèle Optimisée pour le Cache
P.S. : J'ai également réalisé des expériences pour améliorer les performances de uBLAS. En fait, il est assez simple d'améliorer (oui, jeu de mots :) ) les performances de uBLAS :
Expériences sur uBLAS.
Voici un projet similaire avec BLAZE :
Expériences sur BLAZE.
`
27 votes
BLAS a été optimisé dans tous les sens par des spécialistes du domaine. Je suppose qu'il profite de l'unité de calcul en virgule flottante SIMD de votre puce et utilise de nombreuses astuces pour améliorer le comportement du cache également...
7 votes
Encore comment effectuer 1E10 opérations sur un processeur à 2.63E9 cycles/seconde en 1.3 secondes ?
15 votes
Unités d'exécution multiples, pipe-lining et Données Multiples Instruction Unique ((SIMD) qui signifie effectuer la même opération sur plus d'une paire d'opérandes en même temps). Certains compilateurs peuvent cibler les unités SIMD sur les puces courantes mais vous devez presque toujours l'activer explicitement, et il est utile de savoir comment tout cela fonctionne (fr.wikipedia.org/wiki/SIMD). S'assurer contre les ratés de cache est presque certainement la partie la plus difficile.
1 votes
Très intéressant, c'est une bonne chose que je n'ai pas pour objectif de réimplémenter BLAS. Ça me dérangeait vraiment qu'ils effectuaient plus d'opérations par seconde que la vitesse de mon processeur. Mais SIMD pourrait certainement être la clé, c'est aussi la bibliothèque MKL qui est optimisée pour les systèmes Intel (ce que j'ai).
15 votes
L'hypothèse est fausse. Il existe de meilleurs algorithmes connus, consultez Wikipedia.
1 votes
Ou la question devrait-elle être "À quel point les appels de processeur normaux sont-ils inefficaces" ? ;)
3 votes
@DeusAduro : Dans ma réponse pour Comment écrire un produit de matrices matrices qui peut rivaliser avec Eigen? j'ai posté un petit exemple sur la manière de mettre en œuvre un produit de matrices matricielles efficace en cache.
1 votes
Question sur cette performance améliorée SIMD : Est-ce ce que les gens désignent par vectorisation? Ou est-ce simplement une parallélisation standard?
1 votes
De retour en 09 (quand vous semblez avoir posé la question), il était courant d'avoir au moins un CPU dual (2) core et parfois un quad (4) core. Dans une situation idéale, le temps d'exécution pourrait être divisé par le nombre de cœurs si vous aviez écrit une application pouvant utiliser tous les cœurs. Ainsi, par exemple, 4 secondes pourraient devenir 1 seconde si ce que vous avez calculé pouvait être divisé de manière vraiment agréable entre les unités de calcul et si vous aviez, disons, 4 cœurs.