CUDA:统一内存和指针地址的变化?

Posted

技术标签:

【中文标题】CUDA:统一内存和指针地址的变化?【英文标题】:CUDA: Unified Memory and change of pointer address? 【发布时间】:2020-06-03 00:24:47 【问题描述】:

我正在使用 cuBlas 为一些矩阵运算创建一个库。我首先实现了一个矩阵乘法

库头类片段(.h文件)


#include "cusolverDn.h"  // NOLINT
#include "cuda_runtime.h"  // NOLINT
#include "device_launch_parameters.h"  // NOLINT

namespace perception_core 
namespace matrix_transform 

class CudaMatrixTransformations 
 public:
  CudaMatrixTransformations();

  ~CudaMatrixTransformations();

  void MatrixMultiplicationDouble(double *A, double *B, double *C, const int m, const int k, const int n);

 private:
  // Cublas stuff
  cudaError_t cudaStat1;
  cudaError_t cudaStat2;
  cublasHandle_t cublasH;
  cublasStatus_t cublas_status;


;

  // namespace matrix_transform
  // namespace perception_core

#endif  // LIB_CUDA_ROUTINES_INCLUDE_MATRIX_TRANSFORMS_H_

用于乘法的库类实现片段(.cu 文件)

// This calculates the matrix mult C(m,n) = A(m,k) * B(k,n)
void CudaMatrixTransformations::MatrixMultiplicationDouble(
    double *A, double *B, double *C, int m, int k, const int n) 

      // Calculate size of each array
      size_t s_A = m * k;
      size_t s_B = k * n;
      size_t s_C = m * n;

      // Create the arrays to use in the GPU
      double *d_A = NULL;
      double *d_B = NULL;
      double *d_C = NULL;


      // Allocate memory
      cudaStat1 = cudaMallocManaged(&d_A, s_A * sizeof(double));
      cudaStat2 = cudaMallocManaged(&d_B, s_B * sizeof(double));
      assert(cudaSuccess == cudaStat1);
      assert(cudaSuccess == cudaStat2);
      cudaStat1 = cudaMallocManaged(&d_C, s_C * sizeof(double));
      assert(cudaSuccess == cudaStat1);

      // Copy the data to the device data
      memcpy(d_A, A, s_A * sizeof(double));
      memcpy(d_B, B, s_B * sizeof(double));

      // Set up stuff for using CUDA
      int lda = m;
      int ldb = k;
      int ldc = m;
      const double alf = 1;
      const double bet = 0;
      const double *alpha = &alf;
      const double *beta = &bet;

      cublas_status = cublasCreate(&cublasH);
      assert(cublas_status == CUBLAS_STATUS_SUCCESS);

      // Perform multiplication
        cublas_status = cublasDgemm(cublasH, // CUDA handle
        CUBLAS_OP_N, CUBLAS_OP_N, // no operation on matrices
        m, n, k, // dimensions in the matrices
        alpha, // scalar for multiplication
        d_A, lda, // matrix d_A and its leading dim 
        d_B, ldb, // matrix d_B and its leading dim 
        beta, // scalar for multiplication
        d_C, ldc // matrix d_C and its leading dim 
        );

      cudaStat1 = cudaDeviceSynchronize();
      assert(cublas_status == CUBLAS_STATUS_SUCCESS);
      assert(cudaSuccess == cudaStat1);

        // Destroy the handle
        cublasDestroy(cublasH);

      C = (double*)malloc(s_C * sizeof(double));
      memcpy(C, d_C, s_C * sizeof(double));

      // Make sure to free resources
      if (d_A) cudaFree(d_A);
      if (d_B) cudaFree(d_B);
      if (d_C) cudaFree(d_C);

      return;
  

CudaMatrixTransformations::CudaMatrixTransformations() 
    cublas_status = CUBLAS_STATUS_SUCCESS;
    cudaStat1 = cudaSuccess;
    cudaStat2 = cudaSuccess;
  

然后我创建了一个 gtest 程序来测试我的功能。我在MatrixMultiplicationDouble 函数中传递了double *result = NULL; 作为C 参数。

gtest 程序片段(.cc 文件)

TEST_F(MatrixTransformsTest, MatrixMultiplication) 
  double loc_q[] = 3, 4, 5, 6, 7 ,8;
  double *q = loc_q;
  double loc_w[] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11;
  double *w = loc_w;
  double *result = NULL;
  double loc_result[M_ROWS * M_COLS] = 14, 50, 86, 122, 23, 86, 149, 212;
  matrix_result = loc_result;

  size_t m = 4;
  size_t k = 3;
  size_t n = 2;

  perception_core::matrix_transform::CudaMatrixTransformations transforms;
  transforms.MatrixMultiplicationDouble(w, q, result, m, k, n);
  auto rr = std::addressof(result);
  printf("\nC addr: %p\n", rr); 

  std::cout << "result:\n";
  print_matrix(result, m, n);
  EXPECT_TRUE(compare<double>(result, matrix_result, m * n));

cuBlas 中的例程运行良好,因为当我在 .cu 文件中打印矩阵时可以看到结果。但是,当我尝试在我的 gtest 文件中访问 result 时,我遇到了 seg 错误。经过进一步检查,我注意到result 指针的地址在.cu.cpp 内部是不同。作为一个例子,我得到:

C addr: 0x7ffc5749db08 (inside .cu)

C addr: 0x7ffc5749dba0 (inside .cpp)

我认为通过使用统一内存,我可以从主机或设备访问该指针。我似乎无法找到有关此地址为何更改并修复 seg 故障问题的答案。关于使用统一内存,我有什么遗漏吗?谢谢!

【问题讨论】:

我的CUDA Runtime API C++ wrappers 中的std::unique_ptr specializations for CUDA 可能会为您提供很好的服务:他们会为您省去错误检查、空值检查和最后的cudaFree()。特别是托管内存有一个unique_ptr 【参考方案1】:

这条线没有做你需要的:

cudaStat1 = cudaMallocManaged(&C, s_C * sizeof(double));

当您修改C 指针的数值时,该修改不会显示在调用环境中。这就是传值的本质,调用CudaMatrixTransformations::MatrixMultiplicationDoubleC指针的数值是传值的

因此该行将在您的函数内部工作(也许),但结果不会以这种方式传递回调用环境。

我建议重新编写您的代码,以便您以类似于处理AB 的方式处理C。创建一个额外的指针d_C,在其上执行cudaMallocManaged,然后在返回之前,memcpy 将来自d_C 的结果返回到C。这假设您在调用此函数之前已为 C 指针正确分配。

还要注意,最后你释放了AB - 这不是你想要的,我不认为。在返回之前,您应该释放 d_Ad_Bd_C

您的代码还有其他问题。例如,您指的是返回 result 指针,但我没有看到任何证据。实际上,我没有看到任何名为 result 的指针。此外,函数原型(在类定义中)与您的实现不匹配。原型建议返回double*,而您的实现返回void

由于我列出了观察结果,我认为您对addressof 的使用不会给您提供您认为的信息。如果要比较数字指针值,则需要比较值本身,而不是存储这些值的位置的地址。

【讨论】:

感谢您的回答。我用你提到的建议更新了代码。即使在从d_CC 执行memcpy 之后,我仍然遇到段错误。我还添加了 .cc 文件,其中我使用了我提到的 result 变量。 print_matrix() 函数只是访问指针内的值,这就是我得到 seg 错误的地方。 您的更新也不起作用。你似乎没有掌握需要什么。 C 指针应该在调用环境中分配,而不是在你的函数中。您不应在 CudaMatrixTransformations::MatrixMultiplicationDouble 中的该指针上使用任何分配函数 谢谢!那行得通!我道歉;当我用 C++ 编写代码时,指针似乎总是困扰着我。

以上是关于CUDA:统一内存和指针地址的变化?的主要内容,如果未能解决你的问题,请参考以下文章

CUDA统一内存

CUDA统一内存

CUDA 内存统一分析

关于CUDA统一虚拟内存的困惑

CUDA统一内存和Windows 10

CUDA中统一内存的函数指针分配