将CUDA cudaMemcpy划分为块

时间:2011-07-25 18:16:08

标签: time cuda transfer memcpy

一位同事和我正在集思广益,讨论如何减少主机和设备之间的内存传输时间,并且可能有助于将事情安排到一个大型转移(即一次呼叫)。这导致我创建了一个测试用例,在这个测试用例中我采用了传输少量大数据块与许多小数据数据块的时序。我得到了一些非常有趣/奇怪的结果,并且想知道这里是否有人有解释?

我不会把我的整个代码放在这里,因为它很长,但我用两种不同的方式测试了这个分块:

  1. 明确写出所有cudaMemcpy,例如:

    cudaEventRecord(start,0);
    cudaMemcpy(aD,a,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 1 * nBytes / 10,a + 1 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 2 * nBytes / 10,a + 2 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 3 * nBytes / 10,a + 3 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 4 * nBytes / 10,a + 4 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 5 * nBytes / 10,a + 5 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 6 * nBytes / 10,a + 6 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 7 * nBytes / 10,a + 7 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 8 * nBytes / 10,a + 8 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaMemcpy(aD + 9 * nBytes / 10,a + 9 * nBytes / 10,nBytes / 10,cudaMemcpyHostToDevice);
    cudaEventRecord(停止,0);
    cudaEventSynchronize(停止);
    cudaEventElapsedTime(& time,start,stop);

  2. 将cudaMemcpy放入for循环:

    cudaEventRecord(start,0);
    for(int i = 0; i< nChunks; i ++)
    {
        cudaMemcpy(aD + i * nBytes / nChunks,a + i * nBytes / nChunks,nBytes / nChunks,     cudaMemcpyHostToDevice);
    }
    cudaEventRecord(停止,0);
    cudaEventSynchronize(停止);
    cudaEventElapsedTime(& time,start,stop);

  3. 需要注意的是,我也在每个测试开始时进行了“热身”转移,以防万一我认为不需要(上下文是由cudaMalloc调用创建的)。

    我测试了总传输大小从1 MB到1 GB,其中每个测试用例传输相同数量的信息,无论它是如何分块的。我的输出样本如下:

      

    单次大转移= 0.451616 ms
      10次​​明确转移= 0.198016 ms
      100次明确转移= 0.691712 ms
      10次​​循环转移= 0.174848 ms
      100次循环转移= 0.683744 ms
      1000次循环转移= 6.145792 ms
      10000次循环转移= 104.981247 ms
      100000循环传输= 13097.441406 ms

    这里有什么有趣的,我没有得到的是,全面的10次转移总是比其他任何转移都要快,甚至是单次大转移!无论数据集有多大或多小,这个结果都保持一致(即10x100MB对1x1GB或10x1MB对比1x10MB仍然导致10x更快)。如果有人知道为什么会这样或者我可能做错了什么来获得这些奇怪的数字,我会非常有兴趣听到你要说的话。

    谢谢!

    P.S。我知道cudaMemcpy带有一个隐式同步,因此我可以使用CPU计时器并且cudaEventSynchronize是多余的,但我认为最好是安全的一面

    更新:我写了一个函数,试图在性能时空连续体中利用这个明显的裂口。但是,当我使用该功能时,在我的测试用例中写成EXACLTY,效果消失了,我看到了我的期望(单个cudaMemcpy最快)。也许这更像是量子物理而不是相对论,其中观察行为会改变行为......

3 个答案:

答案 0 :(得分:4)

cudaMemcpy()是同步的 - 在返回你的app之前,CUDA会等到memcpy完成。

如果您调用cudaMemcpyAsync(),驱动程序将在GPU必须执行memcpy之前将控制权返回给您的应用程序。

调用cudaMemcpyAsync()而不是cudaMemcpy()是至关重要的。并不是因为您希望将传输与GPU处理重叠,而是因为这是获得CPU / GPU并发的唯一方法。

在Amazon EC2中的cg1.4xlarge实例上,驱动程序需要约4微秒来请求GPU的mempy;所以CPU / GPU并发是隐藏驱动程序开销的好方法。

我对你在10点看到的差异没有一个现成的解释 - 我期望看到的主要膝盖是memcpy超过64K的大小。驱动程序将小于64K的memcpy内联到用于提交命令的同一缓冲区中。

答案 1 :(得分:1)

在每次cuda调用之前和之后使用cudaThreadSynchronize()获取实际的内存传输时间,cudaMemcpy()是同步的但不是CPU执行,它取决于所调用的函数。

Cuda函数调用与其他cuda函数调用同步,如其他内存传输或内核执行,这是在CUDA开发人员看不到的不同CUDA线程中管理的。 cudaMemcpyAsync()与其他CUDA调用是异步的,这就是为什么它需要复制的GPU内存段不与其他并发内存传输重叠。

您确定在这种情况下,CUDA执行线程中同步的cudaMemcpy()是否与CPU线程同步?那么取决于cuda函数它可以或不是,但是如果你在测量时间时使用cudaThreadSynchronize函数它肯定会与CPU同步,并且每个步骤的实际时间都会出现。

答案 2 :(得分:0)

也许CUDA如何衡量时间有一些特殊性。您正在测量小于1毫秒的时间,这非常小。 您是否尝试使用基于CPU的计时器计时并比较结果?