VII. CuBLAS▲
VII-A. BLAS▲
CuBLAS est une implémentation de BLAS pour CUDA, qui s'utilise donc sur GPU. Mais qu'est-ce que BLAS ?
Il s'agit d'un ensemble de fonctions standardisées, initialement prévues pour le Fortran, qui servent au calcul algébrique linéaire basique, comme les multiplications de matrices ou de vecteurs.
BLAS n'est pas une librairie en tant que telle : il ne s'agit que d'un standard, qui doit encore être implémenté. En voici quelques-unes, parmi les principales.
- refblas, l'implémentation de référence ;
- ACML, optimisée par AMD ;
- Accelerate, optimisée par Apple ;
- MKL, optimisée par Intel ;
- CuBLAS, prévue pour CUDA, que nous allons étudier.
Cette librairie, comme les deux autres, n'est prévue que pour être utilisée conjointement à CUDA ! Elle peut être utilisée en solo, c'est pour cela qu'elle a été prévue, mais elle peut aussi très bien utiliser des emplacement de mémoire déjà alloués par CUDA !
VII-B. Performances▲
L'implémentation qui est proposée par NVIDIA est, évidemment, optimisée pour les GPU NVIDIA. Ce graphique montre les gains que l'on peut espérer en retirer, face à l'ACML, en utilisant Ecolib, une librairie de calcul spécialement prévue pour les GPU, qui utilise CuBLAS.
Comme vous pouvez voir, dans le cas de multiplications de matrices, les gains peuvent être énormes, s'il y a assez de données en entrée. En effet, pour multiplier de petites matrices, les temps d'envoi des données à la carte graphique et d'initialisation du kernel peuvent être démesurés.
Pourquoi avoir choisi cette librairie pour le comparatif ?
Ceux qui l'ont écrite sont des professionnels, qui passent leur temps à optimiser leurs algorithmes, que ce soit pour
le GPU ou pour le CPU. On peut donc dire que ce test est fiable, vu que les méthodes de calcul pour CPU et GPU sont
optimisées à un très haut niveau, dans les deux cas.
Cependant, lorsque vous ajoutez le fait que les résultats doivent être transportés du GPU vers le CPU, le tableau devient moins parfait.
VII-C. Les trois niveaux▲
BLAS est constitué de 3 niveaux de fonctions, chacun s'occupant de certains calculs.
Le premier niveau prend en charge les opérations entre vecteurs, comme le produit scalaire, ou la norme, de cette forme.

Le second niveau prend en charge les opérations entre vecteurs et matrices, de cette forme.

Mais aussi les opérations avec une matrice triangulaire de cette forme.

Le troisième niveau prend en charge les opérations entre matrices, de cette forme.

Mais aussi les opérations avec une matrice triangulaire de cette forme.

C'est ce niveau trois qui est utilisé pour la multiplication de matrices générales (alias GEMM).
VII-D. Initialisation▲
Cette librairie a besoin d'une initialisation en bonne et due forme, mais celle-ci est extrêmement simple.
cublasInit();Et c'est fait !
Cependant, il peut arriver qu'il y ait des erreurs à l'initialisation. Elles sont renvoyées directement par la fonction. Il n'y a que deux retours possibles à ce stade.
- CUBLAS_STATUS_ALLOC_FAILED : erreur à l'allocation des ressources ;
- CUBLAS_STATUS_SUCCESS : succès.
Et quand la librairie ne sera plus utilisée, il est préférable de l'éteindre.
cublasShutdown();Ceci peut aussi amener son lot d'erreurs.
- CUBLAS_STATUS_NOT_INITIALIZED : les ressources n'ont pas encore été allouées ;
- CUBLAS_STATUS_SUCCESS : succès.
Néanmoins, la récupération des erreurs des autres fonctions de CUBLAS n'est pas aussi facile : les fonctions ne retournent rien elles-mêmes. Il faut faire appel à cublasGetError() pour récupérer l'erreur. Dès qu'une erreur est lue, l'état est remis à CUBLAS_STATUS_SUCCESS, c'est-à-dire succès.
VII-E. La mémoire▲
CUBLAS peut s'interfacer avec CUDA même s'il n'est pas fait pour ça à l'origine. Des blocs mémoires alloués avec CUDA peuvent être utilisés avec CuBLAS.
BLAS fonctionne avec deux types de valeurs : des vecteurs et des matrices. CUBLAS permet donc de travailler avec ces types. Avant de leur mettre une valeur, on commence par allouer un espace en mémoire, puis on le spécialise.
Nous allons ici créer une matrice et la remplir.
// Constantes
#define N (2); // Nombre de lignes et de colonnes de la matrice
#define N2 (N * N); // Produit du nombre de lignes et de colonnes
// Allocation de la mémoire pour la matrice sur l'hôte
float * hote;
hote = (float *) malloc ( N2 * hote[0] );
// Remplissage de la matrice
hote[0] = 14 ; // 1 1
hote[1] = 54 ; // 1 2
hote[2] = 29 ; // 2 1
hote[3] = 36 ; // 2 2
// Allocation de la mémoire pour la matrice sur le périphérique
float * periph = 0;
cublasAlloc(N2, sizeof(periph[0]), (void * *) & periph);
// Copie de la matrice de l'hôte vers le périphérique
cublasSetVector(N2, sizeof(hote[0]), hote[0], 1, periph[0], 1);
// Destruction de la matrice sur l'hôte
free(hote);
// Destruction de la matrice sur le périphérique
cublasFree(periph);CUBLAS ne s'occupe que de matrices dites "colonnes majeures". C'est pourquoi il faut préciser le nombre de lignes. En connaissant le nombre de lignes, on connaît le nombre de colonnes, vu que l'on a l'ensemble des valeurs de la matrice. Ceci nous permet d'utiliser un simple tableau de flottants : chaque valeur du tableau correspond à une valeur de la matrice.
cublasAlloc() crée donc un tableau en mémoire. Les propriétés de ce tableau sont dictées par les paramètres de la fonction.
- int : nombre de cases du tableau ;
- int : taille d'un élément du tableau ;
- void * * : pointeur vers la mémoire à allouer.
cublasSetVector() spécialise ce tableau en vecteur (qui n'est qu'une forme simplifiée de matrice). Voici ses quelques paramètres.
- int : nombre d'éléments à copier du vecteur hôte ;
- int : taille d'un élément de chaque côté ;
- const void * : vecteur hôte ;
- int : nombre d'espaces entre les valeurs du vecteur hôte ;
- void * : vecteur périphérique ;
- int : nombre d'espaces entre les valeurs du vecteur péripérique.
Quand le vecteur est une matrice, ou une partie d'une matrice, l'incrément du vecteur de 1 permet d'accéder à une colonne, de la dimension majeure de la matrice (son nombre de colonnes ou de lignes), à une ligne.
cublasGetVector() fonctionne de manière analogue : le premier vecteur est celui qui va être copié, le second, l'emplacement où le premier va être stocké.
Pour initialiser une vraie matrice, nous pouvons utiliser la fonction cublasSetMatrix(). Voici son prototype.
cublasSetMatrix
(
int rows,
int cols,
int elemSize,
const void *A,
int lda,
void *B,
int ldb
)- rows : nombre de colonnes ;
- cols : nombre de lignes ;
- elemSize : taille d'un élément de la matrice ;
- A : la matrice source ;
- lda : dimension de direction de A ;
- B : matrice destination ;
- ldb : dimension de direction de B.
Un petit exemple.
// Allocation de la matrice source
float * a;
int rows;
int cols;
int lda;
rows = 4;
cols = 4;
lda = cols;
a = (float *) malloc( sizeof(float) * rows * cols);
// Remplissage de la matrice source
// Première colonne
a[0] = 1;
a[1] = 2;
a[2] = 3;
a[3] = 4;
// Deuxième colonne
a[rows ] = 1;
a[rows + 1] = 1;
a[rows + 2] = 1;
a[rows + 3] = 1;
// Troisième colonne
a[rows * 2 ] = 3;
a[rows * 2 + 1] = 4;
a[rows * 2 + 2] = 5;
a[rows * 2 + 3] = 6;
// Quatrième colonne
a[rows * 3 ] = 5;
a[rows * 3 + 1] = 6;
a[rows * 3 + 2] = 7;
a[rows * 3 + 3] = 8;
// Allocation de la matrice destination
float * b = 0;
cublasAlloc(rows * cols, sizeof(b[0]), (void * *) & b);
// Copie de la matrice
cublasSetMatrix (rows, cols, sizeof(float), (const void *) a, lda, (void *) b, lda) ;
// Suppression des matrices
cublasFree(b);
free(a);VII-F. Construction des nom des de fonctions▲
L'implémentation de BLAS a été effectuée à deux niveaux de précision : la précision simple (FP32) et la double précision (FP64).
Il existe aussi des séries de fonctions pour les nombres complexes.
Toutes les combinaisons n'ont pas été implémentées complètement : les fonctions complexes de BLAS1 à double précision et les fonctions complexes de BLAS2 ne le sont pas encore tout à fait. Toutes les autres le sont. Pour plus de précision, cet article précise toutes les combinaisons existantes sous CUDA 2.2.
Ceci a pour conséquence que toutes les fonctions de chaque niveau existent en 4 versions. Voici les préfixes utilisés.
- S : réel, simple précision ;
- C : complexe, simple précision ;
- D : réel, double précision ;
- Z : complexe, double précision.
Ensuite, une série de suffixes, propres à chaque niveau, complètent le nom.
Une fonction de CuBLAS aura donc cet aspect :
cublas + [SDCZ] + suffixe
Par exemple, la fonction du premier niveau qui s'occupe de réels en simple précision et qui effectue un produit scalaire est celle-ci : cublasSdot(). S spécifie le type des vecteurs, dot, l'opération à effectuer.
Il est à noter deux exceptions : les suffixes Ixamax et Ixamin, où le préfixe se met à la place du x.
VII-G. Construction des variables▲
CuBLAS utilise plusieurs types, deux ne sont pas issus du standard du C : cuComplex et cuDoubleComplex. Vous pouvez les créer grâce à deux fonctions.
cuFloatComplex make_cuFloatComplex ( float x, float y ) ;
cuDoubleComplex make_cuDoubleComplex ( double x, double y ) ;Le premier nombre, x, est la partie réelle du complexe ; y, sa partie imaginaire.
Les autres types utilisés sont hérités du standard.
VII-H. BLAS1▲
VII-G-1. Ixamax▲
int cublasIsamax ( int n, const float * x, int incx ) ;
int cublasIdamax ( int n, const double * x, int incx ) ;
int cublasIcamax ( int n, const cuComplex * x, int incx ) ;Trouve l'index de l'élément à la plus grande valeur absolue du vecteur x, qui possède n éléments. Chaque élément est séparé d'un autre de incx.
VII-G-2. Ixamin▲
int cublasIsamin ( int n, const float * x, int incx ) ;
int cublasIdamin ( int n, const double * x, int incx ) ;
int cublasIcamin ( int n, const cuComplex * x, int incx ) ;Trouve l'index de l'élément à la plus petite valeur absolue du vecteur x, qui possède n éléments. Chaque élément est séparé d'un autre de incx.
VII-G-3. asum▲
float cublasSasum ( int n, const float * x, int incx ) ;
float cublasDasum ( int n, const double * x, int incx ) ;
float cublasScasum ( int n, const cuDouble * x, int incx ) ;Retourne la somme des valeurs absolues des éléments du vecteur x, qui possède n éléments. Chaque élément est séparé d'un autre de incx.
VII-G-4. axpy▲
void cublasSaxpy ( int n, float alpha, const float * x, int incx, float * y, int incy ) ;
void cublasDaxpy ( int n, double alpha, const double * x, int incx, double * y, int incy ) ;
void cublasCaxpy ( int n, cuComplex alpha, const cuComplex * x, int incx, cuComplex * y, int incy ) ;
Met, dans y, le résultat de l'opération
, où
est un nombre,
et
sont des vecteurs.
VII-G-5. copy▲
void cublasScopy ( int n, const float * x, int incx, const float * y, int incy ) ;
void cublasDcopy ( int n, const double * x, int incx, const double * y, int incy ) ;
void cublasCcopy ( int n, const cuComplex * x, int incx, const cuDouble * y, int incy ) ;
Copie le vecteur
dans le vecteur
.
VII-G-6. dot▲
float cublasSdot ( int n, const float * x, int incx, const float * y, int incy ) ;
float cublasDdot ( int n, const double * x, int incx, const double * y, int incy ) ;
Calcule le produit scalaire de
et de
. En cas d'erreur, cette fonction retourne
0.0f.
VII-G-7. dotc▲
cuComplex cublasCdotc ( int n, cuComplex float * x, int incx, const float * y, int incy ) ;
Retourne le produit scalaire des vecteurs
et
, le premier étant conjugué.
VII-G-8. dotu▲
cuComplex cublasCdotu ( int n, cuComplex float * x, int incx, const float * y, int incy ) ;
cuComplex cublasZdotu ( int n, cuComplex float * x, int incx, const float * y, int incy ) ;
Retourne le produit scalaire des vecteurs
et
.
VII-G-9. nrm2▲
float cublasSnrm2 ( int n, const float * x, int incx ) ;
float cublasDnrm2 ( int n, const double * x, int incx ) ;
float cublasScnrm2 ( int n, const cuComplex * x, int incx ) ;
Retourne la norme euclidienne du vecteur
.
VII-G-10. rot▲
void cublasSrot ( int n, float * x, int incx, float * y, int incy, float sc, float ss ) ;
void cublasDrot ( int n, double * x, int incx, double * y, int incy, float sc, float ss ) ;
void cublasCrot ( int n, cuComplex * x, int incx, cuComplex * y, int incy, float sc, float ss ) ;
Applique une rotation de matrice
.
Multiplie la matrice
avec la matrice
, qui possède
n colonnes.
VII-G-11. rotg▲
void cublasSrotg ( float * sa, float * sb, float * sc, float * ss ) ;
void cublasDrotg ( double * sa, double * sb, float * sc, float * ss ) ;
void cublasCrotg ( cuComplex * sa, cuComplex * sb, float * sc, float * ss ) ;
Calcule la matrice de transformation de Givens telle que
et que
,
à partir de sa et sb.
Ceci oblige à calculer
,
qui sert à calculer les valeurs cherchées de cette manière :
.
VII-G-12. scal▲
void cublasSscal ( int n, float alpha, float * x, int incx ) ;
void cublasDscal ( int n, double alpha, double * x, int incx ) ;
void cublasCsscal ( int n, cuComplex alpha, cuComplex * x, int incx ) ;
void cublasZscal ( int n, cuDoubleComplex alpha, cuDoubleComplex * x, int incx ) ;
Multiplie le vecteur
par la constante
.
VII-G-13. swap▲
void cublasSrotg ( int n, float * x, int incx, float * y, int incy ) ;
void cublasDrotg ( int n, double * x, int incx, float * y, int incy ) ;
void cublasCrotg ( int n, cuComplex * x, int incx, float * y, int incy ) ;VII-I. BLAS2▲
VII-H-1. gbmv▲
void cublasSgbmv ( char trans, int m, int n, int kl, int ku, float alpha, const float * A, int lda,
const float * x, int incx, float beta, float * y, int incy ) ;
Effectue une opération de la forme
.
trans doit être une de ces valeurs.
- N ou n : la matrice
ne sera pas modifiée avant calcul ; - T ou t : la transposée de la matrice
sera utilisée pour les calculs ; - C ou c : la transposée de la matrice
sera utilisée pour les calculs.
m correspond au nombre de lignes de
,
n, au nombre de colonnes. Ils doivent être au minimum nuls.
kl correspond au nombre de subdiagonales de la matrice
, ku, au nombre de superdiagonales
de la matrice
.



La matrice
a une dimension directrice lda, qui
est son nombre de lignes. lda doit au moins être égal à
.
est le vecteur qui servira à la multiplication, qui doit avoir
au moins
dimensions
si la matrice n'est pas modifée, ou
si sa transposée est utilisée.
est le vecteur qui servira à la multiplication, qui doit avoir
au moins
dimensions
si la matrice n'est pas modifée, ou
si sa transposée est utilisée. C'est dans cette variable que sera stocké le résultat de l'opération.
VII-H-2. gemv▲
void cublasSgemv ( char trans, int m, int n, float alpha, const float * A, int lda,
const float * x, int incx, float beta, float * y, int incy ) ;
void cublasDgemv ( char trans, int m, int n, float alpha, const float * A, int lda,
const float * x, int incx, float beta, float * y, int incy ) ;
void cublasZgemv ( char trans, int m, int n, float alpha, const float * A, int lda,
const float * x, int incx, float beta, float * y, int incy ) ;
Effectue une opération de la forme
.
Identique à la fonction précédente, à l'exception des paramètres
et
.
VII-H-3. sger▲
void cublasSger ( int m, int n, float alpha, const float * x, int incx, const float * y,
int incy, float * A, int lda ) ;
void cublasDger ( int m, int n, double alpha, const double * x, int incx, const double * y,
int incy, double * A, int lda ) ;
Cette fonctione effectue les opérations de cette forme :
![]()
VII-H-4. sbmv▲
void cublasSsbmv ( char uplo, int n, int k, float alpha, const float * A, int lda,
const float * x, int incx, float beta, float * y, int incy ) ;
Effectue les opérations de la forme
,
où
est une matrice de
de côté et symmétrique, possédant
superdiagonales et
subdiagonales. Par exemple, cette matrice peut servir de paramètre,
à condition de donner des valeurs à
,
,
et
.
uplo doit être une de ces valeurs.
- U ou u : utiliser le triangle supérieur de la matrice (rouge) ;
- L ou l : utiliser le triangle inférieur de la matrice (vert).

VII-H-5. spmv▲
void cublasSsbmv ( char uplo, int n, float alpha, const float * AP, const float * x, int incx,
float beta, float * y, int incy ) ;
Effectue les opérations de la forme
,
identitique à sbmv, à l'exception de la disparition du nombre de superdiagonales.
VII-H-6. spr▲
void cublasSspr ( char uplo, int n, float alpha, const float * x, int incx, float * AP ) ;
Effectue les opérations de la forme
.
VII-H-7. spr2▲
void cublasSspr2 ( char uplo, int n, float alpha, const float * x, int incx,
const float * y, int incy, float * AP ) ;
Effectue les opérations de la forme
.
VII-H-8. symv▲
void cublasSsymv ( char uplo, int n, float alpha, const float * A, int lda,
const float * x, int incx, float beta, float * y, int incy ) ;
Effectue les opérations de la forme
.
VII-H-9. syr▲
void cublasSsyr ( char uplo, int n, float alpha, const float * x, int incx,
float * A, int lda ) ;
Effectue les opérations de la forme
.
VII-H-10. syr2▲
void cublasSsyr2 ( char uplo, int n, float alpha, const float * x, int incx,
const float * x, int incx, float * A, int lda ) ;
Effectue les opérations de la forme
.
VII-H-11. tbmv▲
void cublasStbmv ( char uplo, char trans, char diag, int n, const float * A,
int lda, float * x, int incx ) ;
Effectue les opérations de la forme
ou bien
.
La matrice
doit être triangulaire, c'est-à-dire semblable
à celle ci-dessous.

Une matrice est dite unitriangulaire si elle est triangulaire et que toutes les valeurs de sa diagonale principale sont 1.

diag permet de spécifier si la matrice est unitriangulaire (U ou u) ou non (N ou n).
VII-H-12. tbsv▲
void cublasStbsv ( char uplo, char trans, char diag, int n, int k,
const float * A, int lda, float * x, int incx ) ;
Résout les équations de la forme
ou
.
VII-H-13. tpmv▲
void cublasStpmv ( char uplo, char trans, char diag, int n,
const float * AP, float * x, int incx ) ;
Effectue les opérations de la forme
ou bien
.
VII-H-14. tpsv▲
void cublasStpsv ( char uplo, char trans, char diag, int n,
const float * AP, float * x, int incx ) ;
Résout les équations de la forme
ou
.
VII-H-15. trmv▲
void cublasStrmv ( char uplo, char trans, char diag, int n,
const float * A, int lda, float * x, int incx ) ;
Effectue les opérations de la forme
ou bien
.
VII-H-16. trsv▲
void cublasStrsv ( char uplo, char trans, char diag, int n,
const float * A, int lda, float * x, int incx ) ;
Résout les équations de la forme
ou
.
VII-J. BLAS3▲
VII-I-1. gemm▲
void cublasSgemm ( char transa, char transb, int m, int n, int k, float alpha,
const float * A, int lda, const float * B, int ldb,
float beta, float * C, int ldc ) ;
void cublasDgemm ( char transa, char transb, int m, int n, int k, double alpha,
const double * A, int lda, const double * B, int ldb,
double beta, double * C, int ldc ) ;
void cublasCgemm ( char transa, char transb, int m, int n, int k, cuComplex alpha,
const cuComplex * A, int lda, const cuComplex * B, int ldb,
cuComplex beta, cuComplex * C, int ldc ) ;
void cublasZgemm ( char transa, char transb, int m, int n, int k, cuDoubleComplex alpha,
const cuDoubleComplex * A, int lda, const cuDoubleComplex * B, int ldb,
cuDoubleComplex beta, cuDoubleComplex * C, int ldc ) ;
Effectue les opérations de la forme
.
peut être la matrice
, sa transposée
,
ou bien (dans le cas de matrices complexes) le conjugué de sa transposée
.
Les paramètres transa et transb permettent de diriger ce comportement.
- N, n : pas de transformation ;
- T, t : transposée ;
- C, c : conjugué de la transposée.
est une matrice
sur
.
est une matrice
sur
.
est une matrice
sur
.
VII-I-2. symm▲
void cublasSsymm ( char side, char uplo, int m, int n, float alpha, const float * A, int lda,
const float * B, int ldb, float beta, float * C, int ldc ) ;
void cublasDsymm ( char side, char uplo, int m, int n, double alpha, const double * A, int lda,
const double * B, int ldb, double beta, double * C, int ldc ) ;
Effectue les opérations de la forme
(si side vaut L ou l) ou bien
(si side vaut R ou r).
et
sont des matrices
d'ordre
fois
.
est une matrice symmétrique, de dimension
si side vaut L ou
l.
VII-I-3. syrk▲
void cublasSsyrk ( char uplo, char trans, int n, int k, float alpha,
const float * A, int lda, float beta, float * C, int ldc ) ;
void cublasDsyrk ( char uplo, char trans, int n, int k, double alpha,
const double * A, int lda, double beta, double * C, int ldc ) ;
void cublasZsyrk ( char uplo, char trans, int n, int k, cuDoubleComplex alpha,
const cuDoubleComplex * A, int lda, cuDoubleComplex beta, cuDoubleComplex * C, int ldc ) ;
Effectue les opérations de la forme
(si trans vaut N ou n) ou bien
(si trans vaut T, t, C, c).
est une matrice carrée d'ordre
.
Si trans vaut N ou n,
est une matrice d'ordre
sur
.
Si trans vaut T, t, C ou c,
est une matrice d'ordre
sur
.
VII-I-4. syr2k▲
void cublasSsyr2k ( char uplo, char trans, int n, int k, float alpha,
const float * A, int lda, const float * B, int ldb, float beta, float * C, int ldc ) ;
void cublasDsyr2k ( char uplo, char trans, int n, int k, double alpha,
const double * A, int lda, const double * B, int ldb, double beta, double * C, int ldc ) ;
Effectue les opérations de la forme
(si trans vaut N ou n) ou bien
(si trans vaut T, t, C, c).
VII-I-5. trmm▲
void cublasStrmm ( char side, char uplo, char transa, char diag, int m, int n, float alpha,
const float * A, int lda, const float * B, int ldb ) ;
void cublasDtrmm ( char side, char uplo, char transa, char diag, int m, int n, double alpha,
const double * A, int lda, const double * B, int ldb ) ;
Effectue les opérations de la forme
ou bien
.
VII-I-6. trsm▲
void cublasStrsm ( char side, char uplo, char transa, char diag, int m, int n,
float alpha, const float * A, int lda, float * B, int ldb ) ;
void cublasDtrsm ( char side, char uplo, char transa, char diag, int m, int n,
double alpha, const double * A, int lda, double * B, int ldb ) ;
Résout les équations de la forme
ou bien
.
VII-K. Gestion des erreurs▲
Vous pouvez récupérer la dernière erreur apparue avec la fonction cublasGetError(). Celle-ci va vous renvoyer un des codes suivants (chacun accompagné de sa signification).
| Code d'erreur | Signification |
|---|---|
| CUBLAS_STATUS_SUCCESS | Réussite de l'opération. |
| CUBLAS_STATUS_NOT_INITIALIZED | CuBLAS n'a pas été initialisé. |
| CUBLAS_STATUS_ALLOC_FAILED | CuBLAS n'a pas pu allouer de la mémoire (pas assez de mémoire, en général). |
| CUBLAS_STATUS_INVALID_VALUE | Une variable numérique non supportée a été passée à la fonction. |
| CUBLAS_STATUS_ARCH_MISMATCH | Le GPU ne supporte pas les opérations en double précision. |
| CUBLAS_STATUS_MAPPING_ERROR | CuBLAS n'a pas pu accéder au GPU. |
| CUBLAS_STATUS_EXECUTION_FAILED | CuBLAS n'a pas pu exécuter votre demande correctement sur le GPU. |
| CUBLAS_STATUS_INTERNAL_ERROR | Erreur interne. |
VII-L. Exemple▲
De l'utilisation de CuBLAS pour la multiplication de deux matrices aléatoires.
Vous avez pu remarquer que l'entièreté de CuBLAS est contenue dans le fichier cublas.h.
VII-M. Voir aussi▲
VIII. CuFFT▲
VIII-A. La transformée de Fourier▲
Il s'agit d'un algorithme de décomposition d'une séquence de valeurs en composants de différentes fréquences.
En pratique, cela peut servir à décomposer un signal audio pour obtenir la répartition des fréquences en fonction du temps, comme sur votre chaîne Hi-Fi, qui peut décomposer le signal transmis par les baffles en plages de fréquences : 40 Hz, 200 Hz, 1 kHz, 5kHz et 12 kHz, en général. Mais aussi pour votre électrocardiogramme !
La transformée permet d'obtenir la répartition des fréquences qui caractérisent toutes les ondes qui composent l'onde totale. Le résultat s'appelle un diffractogramme.
Cet exemple se limite aux éléments à une seule dimension. Or, notre univers est constitué de trois dimensions visibles. Cette transformée peut aussi s'appliquer à des éléments à deux ou trois dimensions.
Un exemple d'élément à deux dimensions ? Une image, tout simplement. La transformée permet de passer de l'espace réel (décrit avec des distances) à l'espace réciproque (décrit avec des fréquences). Les plus fins détails de l'image seront transcrits par les plus hautes fréquences de la réciproque.
Concernant la troisième dimension, cette transformée peut servir à la résolution d'équations différentielles partielles en trois dimensions.
VIII-B. La transformée rapide de Fourier (FFT)▲
Il s'agit d'un algorithme de calcul de la transformée discrète de Fourier, nettement plus rapide. Cet algorithme est le plus utilisé en traitement numérique de signal.
Cette transformée est un cas particulier des transformées de Fourier, qui translatent une fonction complexe d'une variable réelle en une autre fonction, la représentation du domaine fréquentiel (ou de fréquence), qui décrit quelles sont les fréquences dans la fonction d'origine.
Ces algorithmes (car il y en a plusieurs) sont plus rapides, mais aussi parfois plus précis dans certains cas, d'autres algorithmes permettent une approximation très approchée des valeurs réelles.
CuFFT permet, à l'heure actuelle, des transformations en une, deux ou trois dimensions, de valeurs réelles (flottantes) ou bien complexes. Des transformations à une dimension peuvent être effectuées en parallèle.
Les données à traiter sont limitées quant à leur taille : pour une seule dimension, seuls huit millions d'éléments sont supportés. Pour deux et trois dimensions, les tailles sont limitées à l'intervalle [ 2 ; 16384 ].
VIII-C. Performances▲
Ici, CuFFT sera comparé à FFTW, une des librairies les plus rapides pour CPU, selon les quelques comparatifs produits avec BenchFFT.
Nous pouvons voir très aisément que le CPU reste en très bonne place, pour de petites quantités de données. Cependant, lorsque l'on commence à avoir des paquets plus importants, le GPU s'impose très vite.
À propos de ce GPU, le graphique montre aussi à quel point les transferts entre CPU et GPU peuvent être lents : avec ces transferts, beaucoup moins de données sont traitées, mais toujours plus qu'un CPU, à partir d'un certain point.
Ici, CuFFT est comparé à Intel MKL, une librairie prévue pour les mathématiques (sur processeurs Intel, pour de meilleurs résultats). Ici, côté CPU, on pourrait dire que l'algorithme est très fort optimisé, mais ce n'est pas suffisant pour détrôner le GPU, qui reste le plus rapide.
VIII-D. Plans▲
CuFFT est basé sur FFTW. Ainsi, les fonctions ne traitent que des pointeurs sur des plans (* cufftHandle).
Mais qu'est-ce qu'un plan ? Un plan est une configuration pour effectuer un calcul, connue à l'avance, ce qui permet d'optimiser un maximum les calculs.
Il existe trois fonctions pour créer des plans : cufftPlan1d(), cufftPlan2d() et cufftPlan3d(), dont voici les prototypes.
cufftResult cufftPlan1d ( cufftHandle * plan, int nx, cufftType type, int batch ) ;
cufftResult cufftPlan3d ( cufftHandle * plan, int nx, int ny, cufftType type ) ;
cufftResult cufftPlan2d ( cufftHandle * plan, int nx, int ny, int nz, cufftType type ) ;Le plan qui sera créé aura un pointeur plan. Il contiendra nx éléments sur l'axe des abscisses, ny sur l'axe des ordonnées et nz sur l'axe des cotes (suivant le nombre de dimensions du plan). Le plan sera transformé batch fois (seulement dans le cas de plans à une dimension). Le type de transformation sera type, qui peut avoir ces quelques valeurs.
- CUFFT_C2C : complexe vers complexe ;
- CUFFT_C2R : complexe vers réel ;
- CUFFT_R2C : réel vers complexe.
Un plan peut être détruit grâce à cufftResult cufftDestroy ( cufftHandle plan ) ;.
Les valeurs des plans peuvent être, soit réelles, soit complexes. Les types associés sont cufftReal et cufftComplex.
VIII-E. Transformation▲
Il existe trois fonctions permettant d'effectuer la transformation, correspondant aux trois valeurs possibles de type pour les fonctions de création de plan.
cufftResult cufftExecC2C ( cufftHandle plan, cufftComplex * idata, cufftComplex * odata, int direction ) ;
cufftResult cufftExecR2C ( cufftHandle plan, cufftReal * idata, cufftComplex * odata ) ;
cufftResult cufftExecC2R ( cufftHandle plan, cufftComplex * idata, cufftReal * odata ) ;idata est un pointeur vers les données à traiter, odata est un pointeur vers l'emplacement où les valeurs traitées seront stockées.
direction spécifie la direction de l'algorithme : soit vers l'avant (CUFFT_FORWARD), soit vers l'arrière (CUFFT_BACKWARD).
VIII-F. Gestion des erreurs▲
Contrairement à CUDA et à CuBLAS, CuFFT n'utilise pas une fonction spécifique pour récupérer les erreurs. Chaque fonction renvoie un cufftResult, qui peut avoir une de ces valeurs.
- CUFFT_SUCCESS : opération effectuée correctement ;
- CUFFT_INVALID_PLAN : plan passé en argument invalide ;
- CUFFT_ALLOC_FAILED : erreur lors de l'allocation de la mémoire ;
- CUFFT_INVALID_TYPE : type demandé non supporté ;
- CUFFT_INVALID_VALUE : mauvais pointeur mémoire ;
- CUFFT_INTERNAL_ERROR : erreur interne ;
- CUFFT_EXEC_FAILED : erreur d'exécution de la transformée sur le GPU ;
- CUFFT_SETUP_FAILED : erreur d'initialisation de la librairie ;
- CUFFT_SHUTDOWN_FAILED : erreur de fermeture de la librairie ;
- CUFFT_INVALID_SIZE : taille de plan non supportée.
VIII-G. Exemples▲
Pour une transformation de complexe vers complexe à une dimension, sans gestion des erreurs.
Pour une transformation de complexe vers complexe à deux dimensions.
Ces exemples ne disent pas une chose : pour pouvoir utiliser CuFFT, il faut inclure le fichier cufft.h.
VIII-H. Voir aussi▲
IX. CUTIL▲
CUDA Utility Library.
Afin de bien comprendre CUDPP, il faut connaître, au moins un peu, CUTIL, qui simplifie le développement. Cette librairie est, à l'origine, prévue pour raccourcir le temps de développement des exemples du SDK. Ainsi, elle supporte diverses choses :
- Parser la ligne de commande ;
- Lire et écrire des fichiers binaires et des images aux formats PPM et PGM ;
- Comparer des tableaux de données (pour la comparaison des résultats entre CPU et GPU) ;
- Mesurer le temps ;
- Chercher des erreurs dans le code, par des macros ;
- Rechercher les conflits dans les banques de mémoire partagée.
Comme elle est utilisée dans CUDPP et que sa compilation est fort simple, elle est reléguée à la prochaine partie.
Toutes ces macros et fonctions sont disponibles dans les fichiers cutil.h et cutil_gl_error.h
IX-A. Macros▲
Ces macros ne remplissent leur office que lorsque l'application est compilée en mode de débogage !
Si elle est compilée en Release, la macro n'aura aucun impact sur les performances.
En cas d'erreur, elles ferment automatiquement l'application, en même temps qu'elles écrivent dans le
flux stderr.
IX-A-1. CUT_DEVICE_INIT▲
Cette macro ne prend pas de paramètres. Elle initialise le premier périphérique disponible pour CUDA. En cas de compilation en mode d'émulation, cette fonction ne fait rien.
CUT_DEVICE_INIT();Résultera, en cas d'erreur, en ceci, dans le flux stderr.
There is no device.Ou bien, selon le cas.
There is no device supporting CUDA.Cette macro est la seule à ne pas changer en fonction du mode de compilation !
IX-A-2. CUDA_SAFE_CALL et CUDA_SAFE_CALL_NO_SYNC▲
Ces macros prennent, comme paramètre, un appel à une fonction du runtime de CUDA. En cas d'erreur, elles écrivent l'erreur dans stderr et ferment proprement l'application.
La première commence par synchroniser les threads, la seconde passe cette étape.
CU_SAFE_CALL( cuFree );
CU_SAFE_CALL_NO_SYNC( cuFree );Résultera, en cas d'erreur, en ceci, dans le flux stderr.
Cuda runtime error %x in file __FILE__ in line __LINE__.
Cuda runtime error %x in file __FILE__ in line __LINE__.IX-A-3. CU_SAFE_CALL et CU_SAFE_CALL_NO_SYNC▲
Ces macros prennent, comme paramètre, un appel à une fonction du driver de CUDA. En cas d'erreur, elles écrivent l'erreur dans stderr et ferment proprement l'application.
CU_SAFE_CALL( cuFree );
CU_SAFE_CALL_NO_SYNC( cuFree );Résultera, en cas d'erreur, en ceci, dans le flux stderr.
Cuda driver error %x in file __FILE__ in line __LINE__.
Cuda driver error %x in file __FILE__ in line __LINE__.IX-A-4. CUT_SAFE_CALL▲
Cette macro prend, comme paramètre, un appel à une fonction du driver de CUDA. En cas d'erreur, elles écrivent l'erreur dans stderr et ferment proprement l'application.
CUT_SAFE_CALL( cutFree );Résultera, en cas d'erreur, en ceci, dans le flux stderr.
Cut error in file __FILE_ in line __LINE__.IX-A-5. CUT_CHECK_ERROR▲
Cette macro prend en paramètre le message d'erreur à afficher dans stderr.
CUT_CHECK_ERROR("'not enough memory'");Résultera, en cas d'erreur, en ceci, dans le flux stderr.
Cuda error: 'not enough memory' in file __FILE__ in line __LINE__ : %x.IX-A-6. CUT_CHECK_ERROR_GL▲
Cette macro ne prend pas de paramètre et affiche l'erreur OpenGL.
CUT_CHECK_ERROR_GL();Résultera, en cas d'erreur, en ceci, dans le flux stderr.
GL Error in file __FILE__ in line __LINE__ :
%sIX-A-7. CUFFT_SAFE_CALL▲
Cette macro prend, en paramètre, un appel à une fonction de CuFFT et affiche une erreur, s'il y a lieu.
CUFFT_SAFE_CALL( cufftPlan1d(&plan, NX, CUFFT_C2C, BATCH) );Résultera, en cas d'erreur, en ceci, dans le flux stderr.
CUFFT error in file __FILE__ in line __LINE__.IX-A-8. CUT_SAFE_MALLOC▲
Cette macro prend en paramètre un appel à malloc() et retourne, le cas échéant, une erreur dans le flux stderr.
CUT_SAFE_MALLOC( cudaMalloc( (void **) & buffer, size)) ;Résultera, en cas d'erreur, en ceci, dans le flux stderr.
Host malloc failure in file __FILE__ in line __LINE__.IX-A-9. CUT_CONDITION▲
Prend, en paramètre, une condition et ferme l'application si elle n'est pas remplie.
CUT_CONDITION(0 == 0);IX-A-10. CUT_BANK_CHECKER▲
IX-B. Fonctions▲
IX-B-1. Gestion de la mémoire▲
IX-B-1-a. cutFree▲
Cette fonction détruit le pointeur sur une mémoire allouée avec CUTIL qui lui est passé comme paramètre. Si un pointeur a la valeur NULL, cette fonction doit d'abord être utilisée sur lui avant qu'il ne puisse être utilisé avec une autre fonction.
void cutFree ( void * ptr ) ;IX-B-2. Gestion des fichiers▲
IX-B-2-a. cutFindFilePath()▲
Trouve le chemin du fichier filename, le chemin vers l'exécutable de votre application étant executablePath. La fonction retourne, soit le chemin vers le fichier, soit 0.
char * cutFindFilePath ( const char * filename, const char * executablePath ) ;IX-B-2-b. cutReadFilef() et dérivés▲
Retourne, dans data, pointeur non initialisé, un pointeur vers les données de filename qui ont été lues. len est la longueur de ces données. La fonction retourne CUTTrue si tout s'est bien passé, CUTFalse sinon.
CUTBoolean cutReadFilef ( const char * filename, float ** data, unsigned int * len,
bool verbose = false ) ;
CUTBoolean cutReadFiled ( const char * filename, double ** data, unsigned int * len,
bool verbose = false ) ;
CUTBoolean cutReadFilei ( const char * filename, int ** data, unsigned int * len,
bool verbose = false ) ;
CUTBoolean cutReadFileui ( const char * filename, unsigned int ** data, unsigned int * len,
bool verbose = false ) ;
CUTBoolean cutReadFileb ( const char * filename, char ** data, unsigned int * len,
bool verbose = false ) ;
CUTBoolean cutReadFileub ( const char * filename, unsigned char ** data, unsigned int * len,
bool verbose = false ) ;IX-B-2-c. cutWriteFilef() et dérivés▲
Retourne, dans data, pointeur non initialisé, un pointeur vers les données de filename qui ont été lues. len est la longueur de ces données. epsilon sert à préciser la précision. La fonction retourne CUTTrue si tout s'est bien passé, CUTFalse sinon.
CUTBoolean cutWriteFilef ( const char * filename, const float * data,
unsigned int len, float epsilon, bool verbose = false ) ;
CUTBoolean cutWriteFiled ( const char * filename, const double * data,
unsigned int len, double epsilon, bool verbose = false ) ;
CUTBoolean cutWriteFilei ( const char * filename, const int * data,
unsigned int len, int epsilon, bool verbose = false ) ;
CUTBoolean cutWriteFileui ( const char * filename, const unsigned int * data,
unsigned int len, unsigned int epsilon, bool verbose = false ) ;
CUTBoolean cutWriteFileb ( const char * filename, const char * data,
unsigned int len, char epsilon, bool verbose = false ) ;
CUTBoolean cutWriteFileub ( const char * filename, const unsigned char * data,
unsigned int len, unsigned char epsilon, bool verbose = false ) ;IX-B-2-d. cutLoadPGMub et dérivés, cutLoadPPM(4)ub▲
Lit l'image file dans le pointeur data. Elle possède h pixels de large et w pixels de large.
CUTBoolean cutLoadPGMub ( const char * file, unsigned char ** data, unsigned int * w,
unsigned int * h ) ;
CUTBoolean cutLoadPPMub ( const char * file, unsigned char ** data, unsigned int * w,
unsigned int * h ) ;
CUTBoolean cutLoadPPM4ub ( const char * file, unsigned char ** data, unsigned int * w,
unsigned int * h ) ;
CUTBoolean cutLoadPGMi ( const char * file, unsigned int ** data, unsigned int * w,
unsigned int * h ) ;
CUTBoolean cutLoadPGMs ( const char * file, unsigned short ** data, unsigned int * w,
unsigned int * h ) ;
CUTBoolean cutLoadPGMf ( const char * file, unsigned float ** data, unsigned int * w,
unsigned int * h ) ;IX-B-2-e. cutSavePGMub et dérivés▲
Écrit l'image file depuis le pointeur data. Elle possède w pixels de large et h pixels de large.
CUTBoolean cutSavePGMub ( const char * file, unsigned char * data, unsigned int w, unsigned int h ) ;
CUTBoolean cutSavePPMub ( const char * file, unsigned char * data, unsigned int w, unsigned int h ) ;
CUTBoolean cutSavePPM4ub ( const char * file, unsigned char * data, unsigned int w, unsigned int h ) ;
CUTBoolean cutSavePGMi ( const char * file, unsigned int * data, unsigned int w, unsigned int h ) ;
CUTBoolean cutSavePGMs ( const char * file, unsigned short * data, unsigned int w, unsigned int h ) ;
CUTBoolean cutSavePGMf ( const char * file, unsigned float * data, unsigned int w, unsigned int h ) ;IX-B-3. Gestion de la ligne de commande▲
IX-B-3-a. cutCheckCmdLineFlag et dérivés▲
Vérifie si le paramètre flag_name a été passé dans les arguments du programme.
CUTBoolean cutCheckCmdLineFlag ( const int argc, const char ** argv, const char * flag_name ) ;Vérifie si le paramètre flag_name a été passé dans les arguments du programme et met dans val la valeur de cet argument.
CUTBoolean cutGetCmdLineArgumenti ( const int argc, const char ** argv, const char * arg_name,
int * val ) ;
CUTBoolean cutGetCmdLineArgumentf ( const int argc, const char ** argv, const char * arg_name,
float * val ) ;
CUTBoolean cutGetCmdLineArgumentstr ( const int argc, const char ** argv, const char * arg_name,
char ** val ) ;IX-B-4. Comparaison▲
IX-B-4-a. cutComparef et dérivés▲
Compare les deux tableaux de longueur len.
CUTBoolean cutComparef ( const float * reference, const float * data,
const unsigned int len ) ;
CUTBoolean cutComparei ( const int * reference, const int * data,
const unsigned int len ) ;
CUTBoolean cutCompareub ( const unsigned char * reference, const unsigned char * data,
const unsigned int len ) ;IX-B-4-b. cutComparefe▲
Compare les deux tableaux de longueur len, en autorisant une approximation epsilon.
CUTBoolean cutComparefe ( const float * reference, const float * data, const unsigned int len,
const float epsilon ) ;IX-B-5. Gestion du temps▲
CUTBolean cutCreateTimer ( unsigned int * name ) ;
CUTBolean cutDeleteTimer ( unsigned int name ) ;
CUTBolean cutStartTimer ( const unsigned int name ) ;
CUTBolean cutStopTimer ( const unsigned int name ) ;
CUTBolean cutResetTimer ( const unsigned int name ) ;
CUTBolean cutGetTimerValue ( const unsigned int name ) ;Leur utilisation sera explicitée dans l'exemple.
IX-C. Un exemple complet▲
Ceci ne présente que l'utilisation de fonctions et de macros CUTIL. Des fonctions utilisées sont définies dans le fichier completet dans les kernels.
Les fonctions et macros CUTIL sont définies dans le fichier cutil.h.







