我是新來的使用GPU的並行編程,所以如果問題很廣泛或模糊,我很抱歉。我知道有在CULA庫某些並行SVD功能,但應該是什麼策略,如果我有大量相對較小的矩陣因式分解?例如,我有尺寸爲d
的n
矩陣,n
很大,d
很小。如何並行化這個過程?任何人都可以給我一個提示嗎?使用CUDA並行執行多個SVD
回答
您可以查看CULA博客的Batched Operations帖子,以便討論您的問題。
編輯
從我從下面您的評論理解,你希望每個線程計算一個單獨的SVD。所以,基本上每個線程都應該執行一個標準的順序SVD方案。對於一些可能有用的參考資料:
Golub, Van Loan, Matrix Computations
如果你使用這種方法,不過,我怕你將無法再使用CUBLAS,因爲這些都是host
功能不可調用從device
(除非你沒有計算能力>3.5
,看到了simpleDevLibCUBLAS
例子)。但基本上這樣我認爲你自己實現了批處理概念。
如果你決定去一個更標準的並行GPU執行,低於基準可能會感興趣:
我以前的答案是現在外的日期。自2015年2月,CUDA 7(目前在發佈候選版)提供了在其cuSOLVER庫全SVD能力。下面,我提供一個使用CUDA cuSOLVER生成奇異值分解的示例。
關於你正在上升的具體問題(計算小尺寸幾個矩陣的奇異值分解),你應該使用流調整我在下面提供的例子。到流,以每個任務關聯起來,你可以使用
cudaStreamCreate()
和
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__); }
您如何看待這種方法與使用MAGMA? –
@AndreasYankopolus我還沒有比較這兩個庫,對不起。 – JackOLantern
- 1. Cuda並行執行
- 2. CUDA併發執行
- 3. CUDA並行內核執行,每個流有多個內核
- 4. 使用CUDA並行執行多個1D移動平均數推力
- 5. 並行執行多個NSOperation
- 6. 並行執行多個AsyncTask
- 7. 某些子網格未使用CUDA動態並行執行
- 8. 並行化SVD計算C++
- 9. 並行使用openMP的SVD分解不如預期的那樣執行
- 10. CUDA並行化
- 11. CUDA執行時間
- 12. CUDA流和併發內核執行
- 13. cuda瞭解併發內核執行
- 14. 並行執行多個對象方法
- 15. 並行執行多個.jmx文件
- 16. 並行執行多個php腳本
- 17. 使用appium在多個iOS模擬器上並行執行
- 18. 在shell腳本中使用GNU並行執行多個mysql腳本並行
- 19. 使用GNU並行在多個文件中的多行內執行bash腳本
- 20. 運行並行CUDA任務
- 21. Maven:從命令行執行並在配置中執行多個
- 22. 在CUDA中並行運行多個子內核
- 23. 用多個罐子執行執行
- 24. CUDA並行線程
- 25. CUDA塊並行性
- 26. 使用SVD進行圖像壓縮
- 27. CUDA的總線程數(執行一段時間,不是並行)是多少?
- 28. CUDA warp中的線程是否在多處理器上並行執行?
- 29. CUDA扭曲執行效率
- 30. 執行CUDA內核幾次
類似於貼在CUDA的批處理求解器/逆矩陣碼註冊的開發者網站,你可以考慮一個矩陣,每個線程或矩陣,每個線程塊方法。如果批量大且矩陣非常小,這很好。在你的情況下,n和d的典型值是多少? – njuffa
BLAS批處理模式只有矩陣乘法,對不對?我如何將它用於SVD?你能給我一個如何分割GPU中的線程或塊的代碼示例,並讓每個單元並行執行一個SVD?例如,如果n = 500 d = 20。謝謝! –
我編輯了我的帖子。我希望這會有所幫助。 – JackOLantern