从设备写入主机并通知主机

时间:2013-05-06 07:59:36

标签: cuda

将CUDA 5与VS 2012和功能3.5(Titan和K20)配合使用。

在我的内核执行的特定阶段,我想将生成的数据块发送到主机内存并通知主机数据已准备就绪,因此主机将对其进行操作。

我不能等到内核执行结束才能从设备读回数据,因为:

  1. 一旦计算出数据,数据就不再与设备相关,因此没有必要将其保持到最后。
  2. 数据大小太大,无法放在设备内存上,等到最后。
  3. 主机不应该等到内核执行结束才开始处理数据。
  4.   

    你能否指出我必须走的道路以及我必须用来实现我的要求的可能的概念和功能?简而言之,我如何写入主机并通知主机块数据是否已准备好进行主机处理?

    N.B。每个线程不与任何其他线程共享任何生成的数据,它们独立运行。所以,据我所知(如果我错了请纠正我),块,线程和warp的概念不会影响这个问题。或者换句话说,如果他们帮助回答,我可以自由地改变他们的组合。

    以下示例代码显示我正在尝试执行此操作:

    #pragma once
    #include <conio.h>
    #include <cstdio>
    #include <cuda_runtime_api.h>
    
    __global__ void Kernel(size_t length, float* hResult) 
    {
        int tid = threadIdx.x + blockIdx.x * blockDim.x;
        // Processing multiple data chunks
        for(int i = 0;i < length;i++)
        {
            // Once this is assigned, I don't need it on the device anymore.
            hResult[i + (tid * length)] = i * 100;
        }
    
    }
    
    void main()
    {
        size_t length = 10;
        size_t threads = 2;
        float* hResult;
        // An array that will hold all data from all threads
        cudaMallocHost((void**)&hResult, threads * length * sizeof(float));
        Kernel<<<threads,1>>>(length, hResult);
        // I DO NOT want to wait to the end and block to get the data
        cudaError_t error = cudaDeviceSynchronize();
        if (error != cudaSuccess) { throw error; }
        for(int i = 0;i < threads * length;i++)
        {
            printf("%f\n", hResult[i]);;
        }
        cudaFreeHost(hResult);
        system("pause");
    }
    

1 个答案:

答案 0 :(得分:2)

在高级别,在设备上:

  • 您需要将数据写入设备全局内存(先前使用cudaMalloc分配)或直接写入主机内存(之前使用cudaHostAlloc分配)
  • 您可能希望从单个线程块中写入此区域的所有数据,以确保在执行以下步骤之前写入所有数据
  • 然后,您需要在执行以下步骤之前发出threadfence()(如果您正在使用设备全局内存)或threadfence_system()调用(如果使用主机内存)
  • 接下来,您将写入设备全局内存或主机内存中的特殊位置,让我们将其称为邮箱位置,并指定数据已准备好的特定值。
  • 可选择发出另一个threadfence或threadfence_system调用

在主持人身上:

  • 在启动内核之前,主机需要将邮箱位置设置为默认值。
  • 启动内核后,主机线程需要“轮询”邮箱位置,查找指示数据准备就绪的特定值
  • 一旦看到特定值,表明数据已准备就绪,主机就可以使用数据
  • 或者,如果要重复此过程,主机可以将邮箱位置重置为默认值。在使用新数据更新数据块之前,设备可以检查此默认值。

请注意,即使使用上述过程,如果从多个线程块生成/创建数据,仍然需要隐含的设备范围同步。可用的唯一直接的设备范围同步是内核启动(或特别是内核的完成)。从单个线程块复制数据只会将设备范围内同步的要求移出此特定序列(到此序列之前的某个位置)。

你给出的理由并没有真正告诉我,代码无法重构,无法通过内核启动基础在内核启动时创建数据,这样可以巧妙地解决这些问题并消除对上述过程的需求同样。

编辑:回复评论中的问题。 在没有具体示例的情况下,很难更具体地说明如何重构代码以便为每个内核调用提供一个数据块。

让我们看一个图像处理案例,其中我有一个30帧的视频序列存储在全局存储器中。内核将根据某种算法处理每个帧,然后将处理后的数据提供给主机。

在您的提议中,在内核完成处理帧之后,它可以向主机发信号通知数据已就绪,然后继续处理下一帧。问题是,如果帧由多个线程块处理,则没有简单的方法来知道何时完成所有线程块处理该帧。可能需要设备范围的同步屏障,但除了通过内核调用机制之外,它并不方便。但是,大概在这样的内核中我们可能会有这样的序列:

  • while(more_frames)
    • 处理框架
    • 信号主持人
    • 增加帧指针

在重构方法中,我们将循环移出内核,以托管代码:

  • while(more_frames)
    • 调用内核来处理框架
    • 消费框架
    • 增加帧指针

通过执行此操作,内核标记了显示帧处理何时完成所需的显式同步,并且可以使用数据。