gpt4 book ai didi

c++ - 使用 CUDA 并行特征值求解器

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:55:34 30 4
gpt4 key购买 nike

我一直在网上搜索和搜索,但似乎无法找到我正在寻找的答案。我有一个特别的问题。

我正在编辑它以简化问题并希望它更具可读性和可理解性。

假设我有 5000 个 20x20 的对称密集矩阵。我想在 CUDA 中创建一个内核,让每个线程负责计算每个对称矩阵的特征值。

如果可能,CUDA 内核的示例代码会很棒。

如有任何帮助/建议,我们将不胜感激!

谢谢,

乔纳森

最佳答案

I would like to create a kernel in CUDA that will have each thread responsible for calculating the eigenvalues for each of the symmetric matrices.

我怀疑这是否是最快的方法,但它可能适用于非常小的矩阵。即使在这种情况下,也可能会进行一些数据存储优化(跨线程交错全局数据),但这会使事情复杂化。

如前所述,该请求可以映射到“令人尴尬的并行”算法中,其中每个线程处理一个完全独立的问题。我们只需要找到合适的单线程“捐赠者代码”。快速谷歌搜索后,我遇到了 this .修改该代码以以这种与线程无关的方式运行是相当简单的。我们只需要借用 3 个例程(jacobi_eigenvaluer8mat_diag_get_vectorr8mat_identity),并用 __host__ __device__ 适本地装饰这些例程在 GPU 上使用,同时没有其他更改

有问题的代码似乎是由佛罗里达州立大学的 J Burkardt 许可的 GNU LGPL。因此,鉴于此,并关注conventional wisdom我没有在这个答案中包含任何大量的代码。但是您应该能够使用我提供的说明通过实验重建我的结果。

注意:我不确定使用此代码的法律后果是什么,它声称已获得 GNU LGPL 许可。您应该确保遵守 any necessary requirements如果您选择使用此代码或其中的一部分。我在这里使用它的主要目的是展示单线程问题解决器的相对微不足道的“令人尴尬的并行”扩展的概念。

通过 here 重构我的完整代码应该是微不足道的并将 3 个指示的函数复制粘贴到剩余代码骨架中指示的位置。但这不会改变前面提到的任何通知/免责声明。使用它需要您自担风险。

同样,从性能的角度来看,不进行其他更改可能不是最好的主意,但它会产生微不足道的工作量,并且可以作为一个可能有用的起点。一些可能的优化可能是:

  1. 寻找一种数据交错策略,使相邻线程更有可能读取相邻数据
  2. 去掉线程代码中的newdelete函数,用固定分配代替(这很容易做到)
  3. 删除不必要的代码 - 例如计算和排序特征向量的代码,如果不需要该数据

无论如何,使用上面修饰过的捐赠者代码,我们只需要在它周围包裹一个简单的内核 (je),以启动每个线程在单独的数据集(即矩阵)上运行,并且每个线程产生自己的一组特征值(和特征向量 - 对于这个特定的代码库)。

出于测试目的,我将其设计为仅使用 3 个线程和 3 个 4x4 矩阵,但将其扩展到您希望的任意多个矩阵/线程应该是微不足道的。

为了简洁起见,我省略了 the usual error checking ,但我建议您使用它,或者如果您进行了任何修改,至少使用 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
$

输出对我来说似乎是合理的,大部分匹配输出 here .

关于c++ - 使用 CUDA 并行特征值求解器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38112248/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com