Je suis nouveau à la programmation parallèle en utilisant GPU donc je m'excuse si la question est large ou vague. Je suis conscient qu'il existe une fonction SVD parallèle dans la bibliothèque CULA, mais quelle devrait être la stratégie si j'ai un grand nombre de matrices relativement petites à factoriser? Par exemple j'ai n
matrices avec la dimension d
, n
est grand et d
est petit. Comment paralléliser ce processus? Quelqu'un pourrait-il me donner un indice?Mise en œuvre parallèle pour plusieurs SVD en utilisant CUDA
Répondre
Vous pouvez jeter un oeil à l'article Batched Operations du blog CULA pour une discussion de votre problème.
EDIT
D'après ce que je comprends de votre commentaire ci-dessous, vous voulez chaque fil pour calculer une SVD séparée. Donc, fondamentalement, chaque thread devrait exécuter un schéma SVD séquentiel standard. Pour que certains peut-être des références utiles:
Golub, Van Loan, Matrix Computations
Si vous utilisez cette approche, cependant, je crains que vous ne pourrez plus utiliser cuBLAS, comme ceux qui sont host
fonctions non appelable à partir du device
(sauf si vous n'avez pas de capacité de calcul >3.5
, voir l'exemple simpleDevLibCUBLAS
.). Mais fondamentalement de cette façon, je pense que vous mettez en œuvre le concept du lot par vous-même.
Si vous décidez d'aller à une mise en œuvre du GPU parallèle plus standard, la référence ci-dessous pourrait être d'intérêt:
Ma réponse précédente est maintenant hors de ce jour. En février 2015, CUDA 7 (actuellement en version candidate) offre des capacités SVD complètes dans sa bibliothèque cuSOLVER. Ci-dessous, je donne un exemple de génération de la décomposition de la valeur singulière en utilisant CUDA cuSOLVER. En ce qui concerne le problème spécifique que vous soulevez (calculant le SVD de plusieurs matrices de petite taille), vous devez adapter l'exemple ci-dessous en utilisant des flux. Pour associer un flux à chaque tâche que vous pouvez utiliser
cudaStreamCreate()
et
cusolverDnSetStream()
kernel.cu
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include<iostream>
#include<iomanip>
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
#include<math.h>
#include <cusolverDn.h>
#include <cuda_runtime_api.h>
#include "Utilities.cuh"
/********/
/* MAIN */
/********/
int main(){
// --- gesvd only supports Nrows >= Ncols
// --- column major memory ordering
const int Nrows = 7;
const int Ncols = 5;
// --- cuSOLVE input/output parameters/arrays
int work_size = 0;
int *devInfo; gpuErrchk(cudaMalloc(&devInfo, sizeof(int)));
// --- CUDA solver initialization
cusolverDnHandle_t solver_handle;
cusolverDnCreate(&solver_handle);
// --- Setting the host, Nrows x Ncols matrix
double *h_A = (double *)malloc(Nrows * Ncols * sizeof(double));
for(int j = 0; j < Nrows; j++)
for(int i = 0; i < Ncols; i++)
h_A[j + i*Nrows] = (i + j*j) * sqrt((double)(i + j));
// --- Setting the device matrix and moving the host matrix to the device
double *d_A; gpuErrchk(cudaMalloc(&d_A, Nrows * Ncols * sizeof(double)));
gpuErrchk(cudaMemcpy(d_A, h_A, Nrows * Ncols * sizeof(double), cudaMemcpyHostToDevice));
// --- host side SVD results space
double *h_U = (double *)malloc(Nrows * Nrows * sizeof(double));
double *h_V = (double *)malloc(Ncols * Ncols * sizeof(double));
double *h_S = (double *)malloc(min(Nrows, Ncols) * sizeof(double));
// --- device side SVD workspace and matrices
double *d_U; gpuErrchk(cudaMalloc(&d_U, Nrows * Nrows * sizeof(double)));
double *d_V; gpuErrchk(cudaMalloc(&d_V, Ncols * Ncols * sizeof(double)));
double *d_S; gpuErrchk(cudaMalloc(&d_S, min(Nrows, Ncols) * sizeof(double)));
// --- CUDA SVD initialization
cusolveSafeCall(cusolverDnDgesvd_bufferSize(solver_handle, Nrows, Ncols, &work_size));
double *work; gpuErrchk(cudaMalloc(&work, work_size * sizeof(double)));
// --- CUDA SVD execution
cusolveSafeCall(cusolverDnDgesvd(solver_handle, 'A', 'A', Nrows, Ncols, d_A, Nrows, d_S, d_U, Nrows, d_V, Ncols, work, work_size, NULL, devInfo));
int devInfo_h = 0; gpuErrchk(cudaMemcpy(&devInfo_h, devInfo, sizeof(int), cudaMemcpyDeviceToHost));
if (devInfo_h != 0) std::cout << "Unsuccessful SVD execution\n\n";
// --- Moving the results from device to host
gpuErrchk(cudaMemcpy(h_S, d_S, min(Nrows, Ncols) * sizeof(double), cudaMemcpyDeviceToHost));
gpuErrchk(cudaMemcpy(h_U, d_U, Nrows * Nrows * sizeof(double), cudaMemcpyDeviceToHost));
gpuErrchk(cudaMemcpy(h_V, d_V, Ncols * Ncols * sizeof(double), cudaMemcpyDeviceToHost));
std::cout << "Singular values\n";
for(int i = 0; i < min(Nrows, Ncols); i++)
std::cout << "d_S["<<i<<"] = " << std::setprecision(15) << h_S[i] << std::endl;
std::cout << "\nLeft singular vectors - For y = A * x, the columns of U span the space of y\n";
for(int j = 0; j < Nrows; j++) {
printf("\n");
for(int i = 0; i < Nrows; i++)
printf("U[%i,%i]=%f\n",i,j,h_U[j*Nrows + i]);
}
std::cout << "\nRight singular vectors - For y = A * x, the columns of V span the space of x\n";
for(int i = 0; i < Ncols; i++) {
printf("\n");
for(int j = 0; j < Ncols; j++)
printf("V[%i,%i]=%f\n",i,j,h_V[j*Ncols + i]);
}
cusolverDnDestroy(solver_handle);
return 0;
}
Utilities.cuh
#ifndef UTILITIES_CUH
#define UTILITIES_CUH
extern "C" int iDivUp(int, int);
extern "C" void gpuErrchk(cudaError_t);
extern "C" void cusolveSafeCall(cusolverStatus_t);
#endif
Utilities.cu
#include <stdio.h>
#include <assert.h>
#include "cuda_runtime.h"
#include <cuda.h>
#include <cusolverDn.h>
/*******************/
/* iDivUp FUNCTION */
/*******************/
extern "C" int iDivUp(int a, int b){ return ((a % b) != 0) ? (a/b + 1) : (a/b); }
/********************/
/* CUDA ERROR CHECK */
/********************/
// --- Credit to http://stackoverflow.com/questions/14038589/what-is-the-canonical-way-to-check-for-errors-using-the-cuda-runtime-api
void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) { exit(code); }
}
}
extern "C" void gpuErrchk(cudaError_t ans) { gpuAssert((ans), __FILE__, __LINE__); }
/**************************/
/* CUSOLVE ERROR CHECKING */
/**************************/
static const char *_cudaGetErrorEnum(cusolverStatus_t error)
{
switch (error)
{
case CUSOLVER_STATUS_SUCCESS:
return "CUSOLVER_SUCCESS";
case CUSOLVER_STATUS_NOT_INITIALIZED:
return "CUSOLVER_STATUS_NOT_INITIALIZED";
case CUSOLVER_STATUS_ALLOC_FAILED:
return "CUSOLVER_STATUS_ALLOC_FAILED";
case CUSOLVER_STATUS_INVALID_VALUE:
return "CUSOLVER_STATUS_INVALID_VALUE";
case CUSOLVER_STATUS_ARCH_MISMATCH:
return "CUSOLVER_STATUS_ARCH_MISMATCH";
case CUSOLVER_STATUS_EXECUTION_FAILED:
return "CUSOLVER_STATUS_EXECUTION_FAILED";
case CUSOLVER_STATUS_INTERNAL_ERROR:
return "CUSOLVER_STATUS_INTERNAL_ERROR";
case CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED:
return "CUSOLVER_STATUS_MATRIX_TYPE_NOT_SUPPORTED";
}
return "<unknown>";
}
inline void __cusolveSafeCall(cusolverStatus_t err, const char *file, const int line)
{
if(CUSOLVER_STATUS_SUCCESS != err) {
fprintf(stderr, "CUSOLVE error in file '%s', line %d\n %s\nerror %d: %s\nterminating!\n",__FILE__, __LINE__,err, \
_cudaGetErrorEnum(err)); \
cudaDeviceReset(); assert(0); \
}
}
extern "C" void cusolveSafeCall(cusolverStatus_t err) { __cusolveSafeCall(err, __FILE__, __LINE__); }
Que pensez-vous de cette approche par rapport à l'utilisation de MAGMA? –
@AndreasYankopolus Je n'ai pas comparé les deux bibliothèques, désolé. – JackOLantern
- 1. CUDA 5 et CUDA 4.2 en parallèle
- 2. Explication de la mise en œuvre parallèle ou sérielle
- 3. de mise en œuvre du réseau de neurones en utilisant CUDA
- 4. Tâche Mise en œuvre de la bibliothèque parallèle sur mono?
- 5. RESTful mise en œuvre de plusieurs modèles
- 6. junit mise en œuvre de plusieurs coureurs
- 7. recherche parallèle dans CUDA
- 8. mise en œuvre FIFO
- 9. mise en œuvre
- 10. TIdAntiFreeze mise en œuvre
- 11. Vecteur propre en SVD
- 12. History.js mise en œuvre
- 13. BlackBerry LongClickListener mise en œuvre
- 14. mise en œuvre « seulement l'un des » et « non en parallèle » sémantique avec la bibliothèque parallèle de tâches
- 15. Mettre en œuvre un algorithme parallèle pour calculer pi
- 16. Mise en œuvre CSM SPMV bloquée
- 17. Mise à jour DB parallèle en utilisant Python (PostGIS/PostgreSQL)
- 18. Mise en œuvre pour la mise en cache des requêtes
- 19. pca en R avec princomp() et en utilisant SVD()
- 20. Mise en œuvre WCF
- 21. Mise en œuvre AsyncTask
- 22. Mise en œuvre EigenSolver efficace
- 23. Mise en œuvre d'UnitOfWork
- 24. API mise en œuvre
- 25. UIImageView Mise en œuvre
- 26. parcourir plusieurs collections en parallèle
- 27. Saferpay Mise en œuvre
- 28. mise en œuvre Mise en page
- 29. Plusieurs IntentServices fonctionnant en parallèle
- 30. Mise en œuvre Fillo
Analogue au code inverse solveur/matrice batched affichée sur le site du développeur CUDA enregistré, vous pourriez envisager une matrice par thread ou une approche matricielle par thread-bloc. Cela fonctionne bien si la taille du lot est grande et les matrices sont très petites. Quelles sont les valeurs typiques pour n et d dans votre cas? – njuffa
Le mode batch BLAS n'a qu'une multiplication matricielle, n'est-ce pas? Comment puis-je l'utiliser pour SVD? Et pourriez-vous me donner un exemple de code pour partitionner les threads ou les blocs dans le GPU et laisser chaque unité faire un SVD en parallèle? Par exemple si n = 500 d = 20. Merci! –
J'ai modifié mon message. J'espère que cela sera utile. – JackOLantern