CUDA: унифицированная память и смена адреса указателя? - PullRequest
0 голосов
/ 19 февраля 2020

Я использую cuBlas для создания библиотеки для некоторых матричных операций. Сначала я реализовал матрицу mult

Фрагмент класса заголовка библиотеки (файл .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 для проверки моей функции. Где я передал double *result = NULL; в качестве параметра C в моей функции MatrixMultiplicationDouble.

Фрагмент программы gtest (. cc file)

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. Однако, когда я пытаюсь получить доступ к result в моем файле gtest, я получаю ошибку сегмента. При дальнейшей проверке я заметил, что адрес указателя result отличается внутри .cu и .cpp. В качестве примера я получаю:

C addr: 0x7ffc5749db08 (inside .cu)

C addr: 0x7ffc5749dba0 (inside .cpp)

Я думал, что с помощью унифицированной памяти я мог бы получить доступ к этому указателю с хоста или устройства. Я не могу найти ответ о том, почему этот адрес меняется и исправить проблему с ошибкой сегмента. Я что-то упускаю из-за использования Unified Memory? Спасибо!

1 Ответ

2 голосов
/ 19 февраля 2020

Эта строка не делает то, что вам нужно:

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

при изменении числового значения указателя C, , что изменение не будет отображаться в вызывающей среде . Такова природа передачи по значению, и числовое значение указателя C передается по значению при вызове CudaMatrixTransformations::MatrixMultiplicationDouble

Так что эта строка будет работать внутри вашей функции (возможно), но результаты не будут переданы обратно в вызывающую среду таким образом.

Я бы предложил переработать ваш код так, чтобы вы обрабатывали C так же, как вы обрабатываете A и * 1015. *. Создайте дополнительный указатель d_C, сделайте на нем cudaMallocManaged, а затем, прежде чем вернуть, memcpy результаты с d_C обратно на C. Это предполагает, что вы правильно выделяете указатель C перед вызовом этой функции.

Также обратите внимание, что в конце вы освобождаете A и B - это не то, что вы хотите, я не считать. Вы должны освободить d_A, d_B и d_C до возвращения.

Есть и другие проблемы с вашим кодом. Например, вы ссылаетесь на возвращение указателя result, но я не вижу никаких доказательств этого. На самом деле я не вижу указатель с именем result. Кроме того, прототип функции (в определении класса) не соответствует вашей реализации. Прототип предлагает возврат double*, тогда как ваша реализация возвращает void.

И поскольку я перечисляю наблюдения, я не думаю, что вы используете addressof, чтобы дать вам информацию, которая, как вы предполагаете, есть. Если вы собираетесь сравнивать числовые значения указателя, вам нужно сравнить сами значения, а не адрес места, где хранятся эти значения.

...