[英]Eigenvalue solver in parallel using CUDA
我一直在搜索和搜索網絡,但似乎找不到所需的答案。 我有一個特別的問題。
我正在對此進行編輯以簡化問題,並希望它更具可讀性和可理解性。
假設我有5000個20x20對稱密集矩陣。 我想在CUDA中創建一個內核,該內核將使每個線程負責計算每個對稱矩陣的特征值。
如果可能,CUDA內核的示例代碼將非常有用。
任何和所有幫助/建議,將不勝感激!
謝謝,
約翰娜森
我想在CUDA中創建一個內核,該內核將使每個線程負責計算每個對稱矩陣的特征值。
我懷疑這是否是最快的方法,但是對於很小的矩陣來說可能是這樣。 即使在這種情況下,也可能會進行一些數據存儲優化(跨線程交錯全局數據),但這會使事情復雜化。
如前所述,該請求可以映射到“令人尷尬的並行”算法中,其中每個線程處理完全獨立的問題。 我們只需要找到合適的單線程“供體代碼”即可。 谷歌快速搜索后,我遇到了這個問題 。 修改該代碼以這種獨立於線程的方式運行非常簡單。 我們只需要借用3個例程( jacobi_eigenvalue
, r8mat_diag_get_vector
和r8mat_identity
),並使用__host__ __device__
裝飾這些例程以在GPU上使用,而無需進行其他更改 。
有問題的代碼似乎是佛羅里達州立大學J Burkardt許可的GNU LGPL。 因此,考慮到這一點,並且按照傳統的看法,我沒有在此答案中包含任何數量的該代碼。 但是您應該能夠按照我給出的說明實驗性地重建我的結果。
注意:我不確定使用此代碼的法律后果,該代碼聲稱已獲得GNU LGPL許可。 如果您選擇使用此代碼或其一部分,則應確保遵守任何必要的要求 。 我在這里使用它的主要目的是演示單線程問題解決程序的相對瑣碎的“令人尷尬的並行”擴展的概念。
通過轉到此處並將3個指示的函數復制粘貼到其余代碼框架中指示的位置,來重構我的完整代碼應該是微不足道的。 但這不會改變任何前面提到的通知/免責聲明。 需要您自擔風險使用它。
同樣,從性能的角度來看,不進行其他任何更改可能不是最好的主意,但這會導致瑣碎的工作量,並且可能是一個有用的起點。 一些可能的優化可能是:
delete
new
和delete
函數,並用固定分配替換它(這很容易做到) 無論如何,使用上面裝飾的供體代碼,我們只需要在其周圍包裝一個瑣碎的內核( je
),以啟動在單獨的數據集(即矩陣)上運行的每個線程,並且每個線程都會產生自己的特征值集(和特征向量) -針對此特定代碼庫)。
為了測試目的,我精心設計了它只能與3個線程和3個4x4矩陣一起使用,但是將其擴展到任意數量的矩陣/線程應該是微不足道的。
為了簡潔起見, cuda-memcheck
了通常的錯誤檢查 ,但是我建議您使用它,或者如果進行任何修改,至少使用cuda-memcheck
運行代碼。
我還構建了代碼,根據矩陣(即線程)的數量和矩陣尺寸,向上調整設備堆的大小以適應內核中的new
操作。 如果您進行了上述第二次優化,則可能會刪除它。
t1177.cu:
#include <stdio.h>
#include <iostream>
const int num_mat = 3; // total number of matrices = total number of threads
const int N = 4; // square symmetric matrix dimension
const int nTPB = 256; // threads per block
// test symmetric matrices
double a1[N*N] = {
4.0, -30.0, 60.0, -35.0,
-30.0, 300.0, -675.0, 420.0,
60.0, -675.0, 1620.0, -1050.0,
-35.0, 420.0, -1050.0, 700.0 };
double a2[N*N] = {
4.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 3.0, 0.0,
0.0, 0.0, 0.0, 2.0 };
double a3[N*N] = {
-2.0, 1.0, 0.0, 0.0,
1.0, -2.0, 1.0, 0.0,
0.0, 1.0, -2.0, 1.0,
0.0, 0.0, 1.0, -2.0 };
/* ---------------------------------------------------------------- */
//
// the following functions come from here:
//
// https://people.sc.fsu.edu/~jburkardt/cpp_src/jacobi_eigenvalue/jacobi_eigenvalue.cpp
//
// attributed to j. burkardt, FSU
// they are unmodified except to add __host__ __device__ decorations
//
//****************************************************************************80
__host__ __device__
void r8mat_diag_get_vector ( int n, double a[], double v[] )
/* PASTE IN THE CODE HERE, FROM THE ABOVE LINK, FOR THIS FUNCTION */
//****************************************************************************80
__host__ __device__
void r8mat_identity ( int n, double a[] )
/* PASTE IN THE CODE HERE, FROM THE ABOVE LINK, FOR THIS FUNCTION */
//****************************************************************************80
__host__ __device__
void jacobi_eigenvalue ( int n, double a[], int it_max, double v[],
double d[], int &it_num, int &rot_num )
/* PASTE IN THE CODE HERE, FROM THE ABOVE LINK, FOR THIS FUNCTION */
// end of FSU code
/* ---------------------------------------------------------------- */
__global__ void je(int num_matr, int n, double *a, int it_max, double *v, double *d){
int idx = threadIdx.x+blockDim.x*blockIdx.x;
int it_num;
int rot_num;
if (idx < num_matr){
jacobi_eigenvalue(n, a+(idx*n*n), it_max, v+(idx*n*n), d+(idx*n), it_num, rot_num);
}
}
void initialize_matrix(int mat_id, int n, double *mat, double *v){
for (int i = 0; i < n*n; i++) *(v+(mat_id*n*n)+i) = mat[i];
}
void print_vec(int vec_id, int n, double *d){
std::cout << "matrix " << vec_id << " eigenvalues: " << std::endl;
for (int i = 0; i < n; i++) std::cout << i << ": " << *(d+(n*vec_id)+i) << std::endl;
std::cout << std::endl;
}
int main(){
// make sure device heap has enough space for in-kernel new allocations
const int heapsize = num_mat*N*sizeof(double)*2;
const int chunks = heapsize/(8192*1024) + 1;
cudaError_t cudaStatus = cudaDeviceSetLimit(cudaLimitMallocHeapSize, (8192*1024) * chunks);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "set device heap limit failed!");
}
const int max_iter = 1000;
double *h_a, *d_a, *h_v, *d_v, *h_d, *d_d;
h_a = (double *)malloc(num_mat*N*N*sizeof(double));
h_v = (double *)malloc(num_mat*N*N*sizeof(double));
h_d = (double *)malloc(num_mat* N*sizeof(double));
cudaMalloc(&d_a, num_mat*N*N*sizeof(double));
cudaMalloc(&d_v, num_mat*N*N*sizeof(double));
cudaMalloc(&d_d, num_mat* N*sizeof(double));
memset(h_a, 0, num_mat*N*N*sizeof(double));
memset(h_v, 0, num_mat*N*N*sizeof(double));
memset(h_d, 0, num_mat* N*sizeof(double));
initialize_matrix(0, N, a1, h_a);
initialize_matrix(1, N, a2, h_a);
initialize_matrix(2, N, a3, h_a);
cudaMemcpy(d_a, h_a, num_mat*N*N*sizeof(double), cudaMemcpyHostToDevice);
cudaMemcpy(d_v, h_v, num_mat*N*N*sizeof(double), cudaMemcpyHostToDevice);
cudaMemcpy(d_d, h_d, num_mat* N*sizeof(double), cudaMemcpyHostToDevice);
je<<<(num_mat+nTPB-1)/nTPB, nTPB>>>(num_mat, N, d_a, max_iter, d_v, d_d);
cudaMemcpy(h_d, d_d, num_mat*N*sizeof(double), cudaMemcpyDeviceToHost);
print_vec(0, N, h_d);
print_vec(1, N, h_d);
print_vec(2, N, h_d);
return 0;
}
編譯並運行示例:
$ nvcc -o t1177 t1177.cu
$ cuda-memcheck ./t1177
========= CUDA-MEMCHECK
matrix 0 eigenvalues:
0: 0.166643
1: 1.47805
2: 37.1015
3: 2585.25
matrix 1 eigenvalues:
0: 1
1: 2
2: 3
3: 4
matrix 2 eigenvalues:
0: -3.61803
1: -2.61803
2: -1.38197
3: -0.381966
========= ERROR SUMMARY: 0 errors
$
在我看來,輸出似乎是合理的,大部分與此處的輸出匹配。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.