使用CUDA并行的特征值求解器

时间:2016-06-30 00:45:36

标签: c++ c algorithm parallel-processing cuda

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

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

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

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

任何和所有的帮助/建议将不胜感激!

谢谢,

乔纳森

1 个答案:

答案 0 :(得分:1)

  

我想在CUDA中创建一个内核,让每个线程负责计算每个对称矩阵的特征值。

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

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

有问题的代码似乎是由佛罗里达州立大学的J Burkardt授权的GNU LGPL。因此,考虑到这一点,并且在conventional wisdom之后,我没有在此答案中包含任何大量的代码。但您应该能够使用我给出的说明以实验方式重建我的结果。

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

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

同样,从绩效的角度来看,不做任何其他改变可能不是最好的想法,但它会带来微不足道的努力,可以作为一个可能有用的起点。一些可能的优化可能是:

  1. 寻找数据交错策略,以便相邻线程更有可能读取相邻数据
  2. 从线程代码中删除newdelete函数,并将其替换为固定分配(这很容易)
  3. 删除不必要的代码 - 例如计算和排序特征向量的代码,如果不需要该数据
  4. 无论如何,使用上面装饰的捐赠代码,我们只需要在它周围包含一个简单的内核(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匹配。