【问题标题】:CUDA: Find out if host buffer is pinned (page-locked)CUDA:找出主机缓冲区是否被固定(页面锁定)
【发布时间】:2015-05-05 20:05:29
【问题描述】:

我的问题的简短描述如下:

我开发了一个调用 CUDA 内核的函数。我的函数接收指向主机数据缓冲区(内核的输入和输出)的指针,并且无法控制这些缓冲区的分配。

--> 主机数据可能是使用 malloc 或 cudaHostAlloc 分配的。我的函数没有具体告知使用了哪种分配方式。

问题是:我的函数有什么可行的方法来确定主机缓冲区是否被固定/页面锁定(cudaHostAlloc)(常规 malloc)?

我问的原因是,如果它们没有被页面锁定,我想使用 cudaHostRegister() 来使它们(缓冲区)如此,使它们适合流。

我尝试了三种失败的方法: 1- 始终应用 cudaHostRegister():如果主机缓冲区已经固定,这种方式不好 2- 运行cudaPointerGetAttributes(),如果返回错误是cudaSuccess,那么buffers已经pinned,无事可做; else if cudaErrorInvalidValue, apply cudaHostRegister :由于某种原因,这种方式会导致内核执行返回错误 3- 运行 cudaHostGetFlags(),如果返回不成功,则应用 cudaHostRegister :与 2-相同的行为。

在 2- 和 3- 的情况下,错误是“invalid argumentn”

请注意,我的代码当前未使用流,而是始终为整个主机缓冲区调用 cudaMemcpy()。如果我不使用上述三种方式中的任何一种,我的代码都会运行完成,无论主机缓冲区是否被固定。

有什么建议吗?非常感谢。

【问题讨论】:

    标签: c++ memory cuda gpu


    【解决方案1】:

    您的方法 2 应该有效(我认为方法 3 也应该有效)。您可能对如何在这种情况下进行正确的 CUDA 错误检查感到困惑。

    由于您有一个运行时 API 调用失败,如果您在内核调用之后执行 cudaGetLastError 之类的操作,它将显示在 cudaPointerGetAttributes() 调用上之前发生的运行时 API 失败.在您的情况下,这不一定是灾难性的。您要做的是清除该错误,因为您知道它已发生并已正确处理。您可以通过额外调用 cudaGetLastError 来做到这一点(对于这种类型的“非粘性”API 错误,即不暗示 CUDA 上下文损坏的 API 错误)。

    这是一个完整的例子:

    $ cat t642.cu
    #include <stdio.h>
    #include <stdlib.h>
    
    #define DSIZE 10
    #define nTPB 256
    
    #define cudaCheckErrors(msg) \
        do { \
            cudaError_t __err = cudaGetLastError(); \
            if (__err != cudaSuccess) { \
                fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                    msg, cudaGetErrorString(__err), \
                    __FILE__, __LINE__); \
                fprintf(stderr, "*** FAILED - ABORTING\n"); \
                exit(1); \
            } \
        } while (0)
    
    __global__ void mykernel(int *data, int n){
    
      int idx = threadIdx.x+blockDim.x*blockIdx.x;
      if (idx < n) data[idx] = idx;
    }
    
    int my_func(int *data, int n){
    
      cudaPointerAttributes my_attr;
      if (cudaPointerGetAttributes(&my_attr, data) == cudaErrorInvalidValue) {
        cudaGetLastError(); // clear out the previous API error
        cudaHostRegister(data, n*sizeof(int), cudaHostRegisterPortable);
        cudaCheckErrors("cudaHostRegister fail");
        }
      int *d_data;
      cudaMalloc(&d_data, n*sizeof(int));
      cudaCheckErrors("cudaMalloc fail");
      cudaMemset(d_data, 0, n*sizeof(int));
      cudaCheckErrors("cudaMemset fail");
      mykernel<<<(n+nTPB-1)/nTPB, nTPB>>>(d_data, n);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail");
      cudaMemcpy(data, d_data, n*sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMemcpy fail");
      int result = 1;
      for (int i = 0; i < n; i++) if (data[i] != i) result = 0;
      return result;
    }
    
    int main(int argc, char *argv[]){
    
      int *h_data;
      int mysize = DSIZE*sizeof(int);
      int use_pinned = 0;
      if (argc > 1) if (atoi(argv[1]) == 1) use_pinned = 1;
      if (!use_pinned) h_data = (int *)malloc(mysize);
      else {
        cudaHostAlloc(&h_data, mysize, cudaHostAllocDefault);
        cudaCheckErrors("cudaHostAlloc fail");}
      if (!my_func(h_data, DSIZE)) {printf("fail!\n"); return 1;}
      printf("success!\n");
      return 0;
    }
    
    $ nvcc -o t642 t642.cu
    $ ./t642
    success!
    $ ./t642 1
    success!
    $
    

    在你的情况下,我认为你没有像我在我放置评论的那一行那样正确处理 API 错误:

    // clear out the previous API error
    

    如果你省略这一步(你可以尝试注释掉它),那么当你在 case 0 中运行代码时(即在函数调用之前不要使用固定内存),那么你会出现一个“下一个错误检查步骤(在我的情况下是下一个 API 调用,但在你的情况下可能是在内核调用之后)。

    【讨论】:

    • 非常感谢!您的回答对我有用并澄清了事情。非常感激。请注意,使用您指出的技巧时,方法 3- 也有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 2011-08-14
    • 2011-11-02
    • 1970-01-01
    • 2011-12-19
    • 2019-08-19
    • 1970-01-01
    相关资源
    最近更新 更多