CUDA - 零拷贝内存,内存映射文件

6
我正在尝试创建一个映射的内存文件,其中包含uint32_t,然后将其用作CUDA的零拷贝固定内存,如下所示。我已经分配了空间并从文件中映射了内存,但在获取设备指针时出现了cudaErrorInvalidValue错误。我知道这个错误消息(来自API)的意思是:

这表明传递给API调用的一个或多个参数不在可接受的值范围内。

但我很难弄清楚为什么会出现这个问题... 有什么想法吗?提前感谢您的帮助。

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(void) 
{
  struct stat buf;

    …

  uint32_t *data, *dev_data;

  cudaDeviceProp cuda_prop;
  cudaGetDeviceProperties(&cuda_prop, 0);
  if (!cuda_prop.canMapHostMemory) 
    exit(EXIT_FAILURE);

  cudaSetDeviceFlags(cudaDeviceMapHost);


  int data_file = open(data_file_name, O_RDONLY);
  int stat = fstat(sa_file, &buf);
  int data_file_size = buf.st_size;

  err = cudaHostAlloc((void**)&data, data_file_size, cudaHostAllocMapped);
  if (err == cudaErrorMemoryAllocation) exit(EXIT_FAILURE);

  data = (uint32_t*) mmap(0, data_file_size, PROT_READ, MAP_PRIVATE, data_file, 0);

  err = cudaHostGetDevicePointer((void**)&dev_data, (void*)data, 0);
  if (err == cudaErrorMemoryAllocation)
  {
    printf("cudaHostGetDevicePointer - Mem Alloc Err\n"); 
    exit(EXIT_FAILURE);
  }
  else if (err == cudaErrorInvalidValue) //ERROR HERE.
  {
    printf("cudaHostGetDevicePointer - Invalid Val Err\n"); 
    exit(EXIT_FAILURE);
  }

    …

}

1
cudaHostAllocdata分配了一个值,然后您用data = mmap(...)覆盖了该值。由于该值不是由CUDA API提供的,因此cudaHostGetDevicePointer不知道如何处理mmap提供的data的新值。您可以尝试删除cudaHostAlloc行,然后在mmap行之后对data进行cudaHostRegister。我不知道那是否有效。 - Robert Crovella
2
如果GPU驱动程序可以从mmaped文件中注册虚拟分配,我会非常惊讶。 - talonmies
1个回答

4

一个问题是您的程序的逻辑顺序不正确。该行代码向由CUDA API提供的data赋值:

err = cudaHostAlloc((void**)&data, data_file_size, cudaHostAllocMapped);

此行代码将使用新值覆盖原有值:

然后覆盖该值,使用一个新值:

data = (uint32_t*) mmap(0, data_file_size, PROT_READ, MAP_PRIVATE, data_file, 0);

在那时,CUDA API不再将data的值视为固定内存空间,因此当您调用以下函数时:

err = cudaHostGetDevicePointer((void**)&dev_data, (void*)data, 0);

你收到一个错误,因为 data 中包含的值未被识别。

编辑:(基于这个问题) 除了这个问题之外,似乎如果将文件处理从只读改为读写,则可以使该过程正常工作(不会抛出运行时错误)。 这是一个完整的代码(不包含上述逻辑缺陷),演示了这一点(我先前创建了一个大小为566316字节的test.dat文件):

$ cat t706.cu
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

int main(void)
{
  struct stat buf;

  char *dev_data;

  cudaDeviceProp cuda_prop;
  cudaGetDeviceProperties(&cuda_prop, 0);
  if (!cuda_prop.canMapHostMemory)
    exit(EXIT_FAILURE);

  cudaSetDeviceFlags(cudaDeviceMapHost);


  int data_file = open("test.dat", O_RDWR);
  int stat = fstat(data_file, &buf);
  int data_file_size = buf.st_size;
  printf("data_file_size = %d\n", data_file_size);
  char *data = (char *) mmap(0, data_file_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, data_file, 0);
  if (data == MAP_FAILED) {
    printf("mmap failure\n");
    exit(EXIT_FAILURE);}
  cudaError_t err = cudaHostRegister(data, data_file_size, cudaHostRegisterDefault);
  if (err != cudaSuccess) { //ERROR HERE.
    printf("cudaHostRegister fail\n");
    exit(EXIT_FAILURE);}

  err = cudaHostGetDevicePointer((void**)&dev_data, (void*)data, 0);
  if (err == cudaErrorMemoryAllocation)
  {
    printf("cudaHostGetDevicePointer - Mem Alloc Err\n");
    exit(EXIT_FAILURE);
  }
  else if (err == cudaErrorInvalidValue)
  {
    printf("cudaHostGetDevicePointer - Invalid Val Err\n");
    exit(EXIT_FAILURE);
  }

}
$ nvcc -arch=sm_30 -o t706 t706.cu
$ ./t706
data_file_size = 566316
$

谢谢您的尝试和纠正主机分配逻辑!我想最简单的方法可能是使用fread而不是使用mmap... - PidgeyBAWK
1
将标志cudaHostRegisterReadOnly传递给cudaHostRegister()函数,可以将内存映射而无需标记为PROT_WRITE。这不仅使代码更加简洁,而且在我的测试环境中似乎还提高了速度。 - John Kugelman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接