[英]Decrease cuda kernel runtime: dynamic memory allocation of matrices in kernel
我想通過在GPU上並行運行矩陣運算來執行適用於大量較小矩陣的OLS。 我寫的代碼似乎正在運行,但是它比預期的要慢。 當前,盡管在GPU上進行並行計算,但在CPU上的單個線程上運行它所需的時間卻較短。 Nvidia Visual Profiler似乎表明內存分配占用了大量時間。 我懷疑罪魁禍首是內核內部不同大小矩陣的動態內存分配。 我需要有關加快內核運行時間的建議和幫助。
我嘗試對循環中創建的每個矩陣使用new和delete。
這是內核:
__global__
void comb_ols(double *y, double *X, double *R2 ,const unsigned int M, const unsigned int N, int* sub_col, int *sub_size, int* cumulative_size, const unsigned int numberOfCalculations){
int size;
int start_index;
int index = blockIdx.x*blockDim.x+threadIdx.x;
int stride = blockDim.x*gridDim.x;
for(int i = index; i < numberOfCalculations; i+=stride){
size = sub_size[i];
start_index = cumulative_size[i];
double *sub_matrix = new double[M*(1+size)];
for(int j = 0; j < size; j++){
for(int k = 0; k<M; k++){
sub_matrix[k] = 1;
sub_matrix[k + M * (1 + j)] = X[k + M * (sub_col[start_index+j]+1)];
}
}
}
R2[i] = getR2(y,sub_matrix,M,size+1);
delete [] sub_matrix;
}
}
在設備函數getR2中,我們具有以下內容:
__device__
double getR2(double *y, double *X ,const unsigned int M, const unsigned int N) {
// Initilize values
double R2, numerator;
double* A = new double[N*N];
double* IA = new double[N*N];
double* yX = new double[N];
// Generate all components
XtX(X, A, M, N);
LUPDecompose(A, N);
LUPInvert(A, N, IA);
yTX(y, X, yX, M, N);
// Calc R2
numerator = olsR2numerator(yX, IA, N);
R2 = numerator / yTy(y, M);
//R2 = yTy(y,M);
delete[] A;
delete[] IA;
delete[] yX;
return R2;
}
實際的內核調用是這樣的:
com_ols<<<numBlocks, blockSize >>>(Y,X,R2,M,N,sub_columns, sub_size, cumulative_size, numberOfCalculations);
當前,內核運行時間僅為1.4秒,而在單線程cpu上為0.7秒。 我希望內核運行時間會更快,因為它只會循環執行矩陣操作的許多迭代,這對於gpu應該是適當的。 如何分配大小不同的矩陣的內存有些效率低下。 你們怎么說在內核內部動態存儲各種大小的矩陣? 應該如何以最有效的方式完成?
給定代碼的任何其他反饋表示贊賞。
在我看來,以下三個非常簡單的經驗法則適用於此:
如果您看一下代碼,它將違反所有這三個概念。
您清楚地知道(或可以簡單地計算)內核啟動之前sub_size
的最大值是sub_size
。 利用這些先驗知識可以為您帶來好處-為計算預先分配堆內存,該內存足夠大,可以處理數據集中最大的問題,並在線程的生命周期內重新使用它。 您的內核很容易看起來像這樣:
__global__
void comb_ols(double *y, double *X, double *R2 ,const unsigned int M,
const unsigned int N, int* sub_col, int *sub_size, int* cumulative_size,
const unsigned int numberOfCalculations, const int max_size){
int size;
int start_index;
int index = blockIdx.x*blockDim.x+threadIdx.x;
int stride = blockDim.x*gridDim.x;
double *sub_matrix = new double[M*(1+max_size)];
R2scratch temp(1+max_size);
for(int i = index; i < numberOfCalculations; i+=stride){
size = sub_size[i];
start_index = cumulative_size[i];
for(int j = 0; j < size; j++){
for(int k = 0; k<M; k++){
sub_matrix[k] = 1;
sub_matrix[k + M * (1 + j)] = X[k + M * (sub_col[start_index+j]+1)];
}
}
}
R2[i] = getR2(y,sub_matrix,M,size+1,temp);
}
delete [] sub_matrix;
}
設備功能如下:
struct R2scratch
{
double* A;
double* IA;
double* yX;
__device__
R2scratch(int N) {
A = new double[N*N];
IA = new double[N*N];
yX = new double[N];
};
__device__
~R2scratch() {
delete[] A;
delete[] IA;
delete[] yX;
};
};
__device__
double getR2(double *y, double *X ,const unsigned int M, const unsigned int N,
R2scratch &scratch) {
// Initilize values
double R2, numerator;
double* A = scratch.A;
double* IA = scratch.IA;
double* yX = scratch.yX;
// Generate all components
XtX(X, A, M, N);
LUPDecompose(A, N);
LUPInvert(A, N, IA);
yTX(y, X, yX, M, N);
// Calc R2
numerator = olsR2numerator(yX, IA, N);
R2 = numerator / yTy(y, M);
//R2 = yTy(y,M);
return R2;
}
[代碼顯然是在瀏覽器中編寫的,從未編譯和測試,使用風險自負]。
通過這樣做,您可以在許多計算中分攤一次內存分配的成本,這應該比當前方法更有效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.