【问题标题】:CUDA: Modify mapped memory from host while kernel is executingCUDA:在内核执行时从主机修改映射内存
【发布时间】:2013-11-24 14:10:37
【问题描述】:

我想在内核执行时从主机修改一块映射内存,然后从内核中读取这个值。

我正在尝试通过以下方式执行此操作。我有一个这样的内核:

__global__ void kernel(int* d_ptr)
{
    *d_ptr = 1;
    while( *d_ptr);
}

d_ptr 映射到主机可访问的一块内存。

我也有一个类似这样的宿主线程

void run( void* input )
{
    int* h_ptr = (int*)input;
    while( kernel_running)
        *h_ptr = 0;
}

因此,主机线程反复向内核重复读取的位置写入 0,直到它看到 0。理论上,内核应该在读取主机线程写入的值后立即停止。问题是内核从不读取这个 0,所以它永远不会终止。

奇怪的是,如果我像这样在内核中添加打印语句

__global__ void kernel(int* d_ptr)
{
    *d_ptr = 1;
    while( *d_ptr) printf("%d\n", *d_ptr);
}

然后它确实读取 0 并终止。我不知道发生了什么。 cuda programming guide 没有提供很多关于并发访问映射内存的信息,而且也很难找到解决这个问题的问题。任何指针?我正在使用 Windows 并且正在编译

nvcc -g -arch=sm_20 -lineinfo

整个代码如下所示:

bool kernel_running = 0;

__global__ void kernel(int* d_ptr)
{
    *d_ptr = 1;
    while( *d_ptr) printf("%d\n", *d_ptr);
}

void run( void* input )
{
    int* h_ptr = (int*)input;
    while( kernel_running)
    {
        *h_ptr = 0;
    }
}

int main()
{
    // HOST AND DEVICE POINTERS
    int* h_ptr = 0;
    int* d_ptr = 0;

    // INITIALIZE POINTERS
    assert( cudaHostAlloc(&h_ptr, sizeof(int), cudaHostAllocMapped) == cudaSuccess);
    assert( cudaHostGetDevicePointer(&d_ptr, h_ptr, 0) == cudaSuccess);

    // RUN KERNEL
    kernel_running = 1;
    _beginthread( run, 0, h_ptr);
    kernel<<<1,1>>>(d_ptr);
    assert( cudaDeviceSynchronize() == cudaSuccess);
    kernel_running = 0;
}

【问题讨论】:

    标签: c windows cuda


    【解决方案1】:

    建议:

    • 在致电cudaHostAlloc之前添加:

      cudaSetDeviceFlags(cudaDeviceMapHost);
      

      documentation 建议这样做。

    • 将您的标志变量声明为volatile

      __global__ void kernel(volatile int* d_ptr)
      

    以下代码无需使用线程即可为我工作:

    #include <stdio.h>
    
    #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(volatile int *data){
    
      while (*data) {};
      printf("finished\n");
    }
    
    int main(){
    
      int *d_data, *h_data;
      cudaSetDeviceFlags(cudaDeviceMapHost);
      cudaCheckErrors("cudaSetDeviceFlags error");
      cudaHostAlloc((void **)&h_data, sizeof(int), cudaHostAllocMapped);
      cudaCheckErrors("cudaHostAlloc error");
      cudaHostGetDevicePointer(&d_data, h_data, 0);
      cudaCheckErrors("cudaHostGetDevicePointer error");
      *h_data = 1;
      printf("kernel starting\n");
      mykernel<<<1,1>>>(d_data);
      cudaCheckErrors("kernel fail");
      getchar();
      *h_data = 0;
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel fail 2");
      return 0;
    }
    

    【讨论】:

    • 感谢您的回答!我试过你的代码,但是 mykernel 中 *data 的值在它启动时是 0,所以出于某种原因我没有复制 1...
    • 好吧,我发现我在之前的评论中提到的问题来自这样一个事实,因为我在 Windows 上使用 WDDM 驱动程序,内核直到 cudaDeviceSynchronize() 才会启动,在在那一点上,*h_data 为零,所以这就是为什么我立即从内核读取零...我通过在内核调用之后立即触发早期启动来解决这个问题,如下所述:stackoverflow.com/questions/13568805/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-05
    • 2011-09-25
    • 2014-10-05
    • 2013-12-24
    • 2015-01-27
    • 2016-07-25
    • 2012-06-15
    相关资源
    最近更新 更多