从 GPU 复制到 CPU 比从 CPU 复制到 GPU 慢
Posted
技术标签:
【中文标题】从 GPU 复制到 CPU 比从 CPU 复制到 GPU 慢【英文标题】:copy from GPU to CPU is slower than copying CPU to GPU 【发布时间】:2012-10-31 17:36:52 【问题描述】:我开始学习cuda有一段时间了,遇到以下问题
在下面看看我的表现:
复制 GPU
int* B;
// ...
int *dev_B;
//initialize B=0
cudaMalloc((void**)&dev_B, Nel*Nface*sizeof(int));
cudaMemcpy(dev_B, B, Nel*Nface*sizeof(int),cudaMemcpyHostToDevice);
//...
//Execute on GPU the following function which is supposed to fill in
//the dev_B matrix with integers
findNeiborElem <<< Nblocks, Nthreads >>>(dev_B, dev_MSH, dev_Nel, dev_Npel, dev_Nface, dev_FC);
再次复制 CPU
cudaMemcpy(B, dev_B, Nel*Nface*sizeof(int),cudaMemcpyDeviceToHost);
-
将数组 B 复制到 dev_B 只需几分之一秒。
但是将数组 dev_B 复制回 B 需要很长时间。
findNeiborElem 函数涉及每个线程的循环 例如好像是这样的
__ global __ void findNeiborElem(int *dev_B, int *dev_MSH, int *dev_Nel, int *dev_Npel, int *dev_Nface, int *dev_FC)
int tid=threadIdx.x + blockIdx.x * blockDim.x;
while (tid<dev_Nel[0])
for (int j=1;j<=Nel;j++)
// do some calculations
B[ind(tid,1,Nel)]=j// j in most cases do no go all the way to the Nel reach
break;
tid += blockDim.x * gridDim.x;
它非常奇怪的是,将 dev_B 复制到 B 的时间与 j 索引的迭代次数成正比。
例如,如果Nel=5
,则时间约为5 sec
。
当我增加Nel=20
时,时间大约是20 sec
。
我希望复制时间应该独立于需要分配矩阵dev_B
的值的内部迭代。
我还希望从 CPU 到 CPU 复制相同矩阵的时间将是相同的顺序。
你知道出了什么问题吗?
【问题讨论】:
【参考方案1】:你应该使用事件而不是使用clock()来测量时间:
有了事件,你会得到这样的东西:
cudaEvent_t start, stop; // variables that holds 2 events
float time; // Variable that will hold the time
cudaEventCreate(&start); // creating the event 1
cudaEventCreate(&stop); // creating the event 2
cudaEventRecord(start, 0); // start measuring the time
// What you want to measure
cudaMalloc((void**)&dev_B, Nel*Nface*sizeof(int));
cudaMemcpy(dev_B, B, Nel*Nface*sizeof(int),cudaMemcpyHostToDevice);
cudaEventRecord(stop, 0); // Stop time measuring
cudaEventSynchronize(stop); // Wait until the completion of all device
// work preceding the most recent call to cudaEventRecord()
cudaEventElapsedTime(&time, start, stop); // Saving the time measured
编辑:附加信息:
“内核启动在完成之前将控制权返回给 CPU 线程。因此,您的计时结构正在测量内核执行时间以及第二个 memcpy。当在内核之后计时复制时,您的计时器代码正在执行立即,但 cudaMemcpy 正在等待内核在启动之前完成。这也解释了为什么您的数据返回时间测量似乎因内核循环迭代而异。它还解释了为什么花在内核函数上的时间“可以忽略不计” “”。归功于Robert Crovella
【讨论】:
是的,您应该使用这种方法。内核启动在完成之前将控制权返回给 CPU 线程。因此,您的计时结构正在测量内核执行时间以及第二个 memcpy。在内核之后计时复制时,您的计时器代码将立即执行,但 cudaMemcpy 正在等待内核在启动之前完成。这也解释了为什么您的数据返回时间测量似乎因内核循环迭代而异。它还解释了为什么花在内核函数上的时间“可以忽略不计”。【参考方案2】:关于你的第二个问题
B[ind(tid,1,Nel)]=j// j in most cases do no go all the way to the Nel reach
在 GPU 上执行计算时,由于同步的原因,每个完成工作的线程都不会执行任何计算,直到同一个工作组中的所有线程都完成。
换句话说,您需要执行此计算的时间将是最坏情况下的时间,如果 大多数 线程没有一直下降也没关系。
我不确定你的第一个问题,你如何测量时间?我对 cuda 不太熟悉,但我认为当从 CPU 复制到 GPU 时,实现会缓冲您的数据,隐藏所涉及的有效时间。
【讨论】:
感谢您的回答,但让我澄清一下。我纯粹是指在复制程序上花费的时间。花在函数“findNeiborElem”上的时间几乎可以忽略不计。为了测量我使用 start = std::clock(); 的时间cudaMemcpy(B, dev_B, NelNfacesizeof(int),cudaMemcpyDeviceToHost);持续时间 = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; std::cout 这取决于您实际要测量的时间,请记住,大多数操作都是异步执行的。换句话说,当程序返回时,GPU 几乎没有完成任何工作,除非程序发出同步点(cudaMemcpyDeviceToHost 就是这种情况)。这里***.com/questions/3553843/… 的解释比我能解释的更好。 那,数据路径 GPU->CPU 的性能在 CUDA 之前一直是无关紧要的,所以它可能没有那么彻底优化(AGP 甚至有意识地决定传输速度应该是不对称的)。以上是关于从 GPU 复制到 CPU 比从 CPU 复制到 GPU 慢的主要内容,如果未能解决你的问题,请参考以下文章
有没有更好更快的方法来使用推力从 CPU 内存复制到 GPU?