C++ 基于CUDA的特征值并行求解器
我一直在网上搜索,似乎找不到我想要的答案。我有一个特别的问题 我编辑这篇文章是为了简单地解决这个问题,希望它更具可读性和可理解性 假设我有5000个20x20对称密集矩阵。我想在CUDA中创建一个内核,每个线程负责计算每个对称矩阵的特征值 如果可能的话,CUDA内核的示例代码将非常棒 任何和所有的帮助/建议都将不胜感激 谢谢 约翰纳森 我想在CUDA中创建一个内核,每个线程负责计算每个对称矩阵的特征值 我怀疑这是否是最快的方法,但它可能适用于非常小的矩阵。即使在这种情况下,也可能会进行一些数据存储优化(在线程之间交错全局数据),但这会使事情复杂化 如上所述,该请求可以映射到一个“令人尴尬的并行”算法中,其中每个线程处理一个完全独立的问题。我们只需要找到合适的单线程“捐赠代码”。在谷歌快速搜索之后,我发现了。修改代码以这种独立于线程的方式运行非常简单。我们只需要借用3个例程(C++ 基于CUDA的特征值并行求解器,c++,c,algorithm,parallel-processing,cuda,C++,C,Algorithm,Parallel Processing,Cuda,我一直在网上搜索,似乎找不到我想要的答案。我有一个特别的问题 我编辑这篇文章是为了简单地解决这个问题,希望它更具可读性和可理解性 假设我有5000个20x20对称密集矩阵。我想在CUDA中创建一个内核,每个线程负责计算每个对称矩阵的特征值 如果可能的话,CUDA内核的示例代码将非常棒 任何和所有的帮助/建议都将不胜感激 谢谢 约翰纳森 我想在CUDA中创建一个内核,每个线程负责计算每个对称矩阵的特征值 我怀疑这是否是最快的方法,但它可能适用于非常小的矩阵。即使在这种情况下,也可能会进行一些数据存
jacobi_特征值
,r8mat_诊断_获取向量
和r8mat_标识
),并用\uuuuuuuuuu主机设备uuuuuu
适当地装饰这些例程,以便在GPU上使用,而不做其他更改
问题代码似乎是佛罗里达州立大学J Burkardt许可的GNU LGPL。因此,考虑到这一点,我在下面的回答中没有包含任何大量的代码。但是你应该能够用我给出的说明,通过实验重建我的结果
注意:我不确定使用该代码的法律后果是什么,该代码声称是GNU LGPL许可的。如果您选择使用此代码或其部分,则应确保遵守。我在这里使用它的主要目的是演示单线程问题求解器的相对简单的“令人尴尬的并行”扩展的概念
通过将3个指定的函数复制粘贴到剩余代码框架中指定的位置来重建完整代码应该很简单。但这不会改变前面提到的任何通知/免责声明。使用它的风险自负
同样,从性能的角度来看,不进行其他更改可能不是最好的主意,但它只需要少量的工作,并且可以作为一个可能有用的起点。一些可能的优化可能是:
new
和delete
函数,并将其替换为固定分配(这很容易做到)je
),即可启动在单独数据集(即矩阵)上运行的每个线程,并且每个线程生成其自己的特征值集(和特征向量-用于此特定代码库)
为了测试的目的,我已经精心设计了它,只使用3个线程和3个4x4矩阵,但是将它扩展到您希望的任意多个矩阵/线程应该很简单
为了简洁起见,我已经省去了,但我建议您使用它,或者如果您做了任何修改,至少使用cuda memcheck
运行您的代码
我还构建了向上调整设备堆大小的代码,以适应内核中的新操作,具体取决于矩阵(即线程)数量和矩阵维度。如果你在上面提到的第二次优化中工作,你可能会删除它
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;
}
在我看来,输出似乎是合理的,大部分与输出相匹配。Jacobi也是迭代的。也许问题是你最初的猜测。如果你的猜测是正确的,你将需要更少的迭代来收敛到一个解决方案。你只对特征值感兴趣,还是需要特征向量?我只需要特征值。有一些关于并行执行雅各比的论文。这就是我上面提到的。很抱歉给你带来了困惑。为了更好地解释我的情况,我事先进行了其他计算,以获得每个点的某些特征。然后根据这些特征计算特征值。迭代部分只计算这些特征值。我希望这有帮助。让我知道这是否有帮助!我不确定我是否理解“点”的含义。这是方阵中行数/列数的同义词吗?更大的矩阵意味着更多的计算——没有什么会改变这一点。从点上看,这就像我在处理一个500 x 3的矩阵。500是点数(行),3是尺寸(列)。基本上,我想把它放大到一个大于100000 x 3的矩阵或更多。@Johnathan:请将所有这些编辑到你的问题中。正如所写的,它很长,杂乱无章,很难理解。如果你知道N的值,这可能也会有帮助
$ 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
$