奇怪的cudaMemcpyAsync同步行为
以下代码用于测试cudaMemcpyAsync的同步行为奇怪的cudaMemcpyAsync同步行为,cuda,Cuda,以下代码用于测试cudaMemcpyAsync的同步行为 #include <iostream> #include <sys/time.h> #define N 100000000 using namespace std; int diff_ms(struct timeval t1, struct timeval t2) { return (((t1.tv_sec - t2.tv_sec) * 1000000) + (t1.tv_
#include <iostream>
#include <sys/time.h>
#define N 100000000
using namespace std;
int diff_ms(struct timeval t1, struct timeval t2)
{
return (((t1.tv_sec - t2.tv_sec) * 1000000) +
(t1.tv_usec - t2.tv_usec))/1000;
}
double sumall(double *v, int n)
{
double s=0;
for (int i=0; i<n; i++) s+=v[i];
return s;
}
int main()
{
int i;
cudaStream_t strm;
cudaStreamCreate(&strm);
double *h0;
double *h1;
cudaMallocHost(&h0,N*sizeof(double));
cudaMallocHost(&h1,N*sizeof(double));
for (i=0; i<N; i++) h0[i]=99./N;
double *d;
cudaMalloc(&d,N*sizeof(double));
struct timeval t1, t2; gettimeofday(&t1,NULL);
cudaMemcpyAsync(d,h0,N*sizeof(double),cudaMemcpyHostToDevice,strm);
gettimeofday(&t2, NULL); printf("cuda H->D %d takes: %d ms\n",i, diff_ms(t2, t1)); gettimeofday(&t1, NULL);
cudaMemcpyAsync(h1,d,N*sizeof(double),cudaMemcpyDeviceToHost,strm);
gettimeofday(&t2, NULL); printf("cuda D->H %d takes: %d ms\n",i, diff_ms(t2, t1)); gettimeofday(&t1, NULL);
cout<<"sum h0: "<<sumall(h0,N)<<endl;
cout<<"sum h1: "<<sumall(h1,N)<<endl;
cudaStreamDestroy(strm);
cudaFree(d);
cudaFreeHost(h0);
cudaFreeHost(h1);
return 0;
}
但是,包含cudaMemcpyAsync调用的时差表明它们没有与主机同步
cuda H->D 100000000 takes: 0 ms
cuda D->H 100000000 takes: 0 ms
因为cuda分析结果不支持这一点:
method=[ memcpyHtoDasync ] gputime=[ 154896.734 ] cputime=[ 17.000 ]
method=[ memcpyDtoHasync ] gputime=[ 141175.578 ] cputime=[ 6.000 ]
不知道为什么…这里至少发生了两件事 你的第一个观察是:
sum h0: 99
sum h1: 99
向同一流发出的CUDA调用将按顺序执行。如果您希望CUDA调用彼此重叠,则必须将它们发送到单独的流。由于您在同一个流中向设备和从设备发出cuda memcpy,因此它们将按顺序执行。第二个将不会开始,直到第一个完成,即使两个都立即排队。因此,在第一次cudaMemcpy之后,数据是完整的,并且您可以观察到两个数组都生成了正确的和
你们剩下的观察结果也相互一致。您正在报告:
cuda H->D 100000000 takes: 0 ms
cuda D->H 100000000 takes: 0 ms
这是因为这两个异步调用都会立即将控制权返回到主机线程,并且该调用排队等待异步执行到主机执行。然后,调用与进一步的主机执行并行进行。由于控件已立即返回到主机,并且您正在使用基于主机的计时方法对操作进行计时,因此它们似乎花费了零时间
当然,它们实际上并不需要零时间,您的分析器结果表明了这一点。由于GPU与CPU异步执行(在本例中包括cudaMemcpyAsync),探查器将显示cudaMemcpy操作所需的实际实时时间(报告为gputime)以及CPU上的视在时间,即CPU启动报告为cputime的操作所需的时间量。请注意,与gputime相比,cputime非常小,即它几乎是瞬时的,因此基于主机的计时方法报告零时间。但它们实际上并不是零时间完成的,而探查器报告了这一点
如果您使用cudaEvent计时方法,您当然会看到不同的结果,这将更接近您的profiler gputime结果。这里至少有两种情况 你的第一个观察是:
sum h0: 99
sum h1: 99
向同一流发出的CUDA调用将按顺序执行。如果您希望CUDA调用彼此重叠,则必须将它们发送到单独的流。由于您在同一个流中向设备和从设备发出cuda memcpy,因此它们将按顺序执行。第二个将不会开始,直到第一个完成,即使两个都立即排队。因此,在第一次cudaMemcpy之后,数据是完整的,并且您可以观察到两个数组都生成了正确的和
你们剩下的观察结果也相互一致。您正在报告:
cuda H->D 100000000 takes: 0 ms
cuda D->H 100000000 takes: 0 ms
这是因为这两个异步调用都会立即将控制权返回到主机线程,并且该调用排队等待异步执行到主机执行。然后,调用与进一步的主机执行并行进行。由于控件已立即返回到主机,并且您正在使用基于主机的计时方法对操作进行计时,因此它们似乎花费了零时间
当然,它们实际上并不需要零时间,您的分析器结果表明了这一点。由于GPU与CPU异步执行(在本例中包括cudaMemcpyAsync),探查器将显示cudaMemcpy操作所需的实际实时时间(报告为gputime)以及CPU上的视在时间,即CPU启动报告为cputime的操作所需的时间量。请注意,与gputime相比,cputime非常小,即它几乎是瞬时的,因此基于主机的计时方法报告零时间。但它们实际上并不是零时间完成的,而探查器报告了这一点
如果您使用cudaEvent计时方法,您当然会看到不同的结果,这将更接近您的profiler gputime结果。谢谢您的回答。但是根据你的解释,和h1不应该是99,因为h1从来没有在主机上初始化过,对吗?更奇怪的是,如果我把{{{coutYou初始化主机上的h0。然后将h0复制到设备上的d。然后将设备上的d复制到主机上的h1。为什么您认为主机h1在这个序列之后与主机h0不匹配?现在,关于您在注释中的最后一个问题,注释掉该行之所以会产生差异,是因为d到h的最后一个副本主机上的1也是异步的,因此下面的代码立即开始。主机上的d复制到h1和sumall之间存在竞争条件。显然,h0的sumall为复制到h1提供了足够的时间。感谢Robert!我弄糊涂了的是,如果这两个异步调用都将控制权返回到主机线程immedi当然,h1不会从
由于在主机上执行sumallh1时的赛车状况,因此sumallh1不应为99,因为尚未完成复制。感谢您的回答。但是根据你的解释,和h1不应该是99,因为h1从来没有在主机上初始化过,对吗?更奇怪的是,如果我把{{{coutYou初始化主机上的h0。然后将h0复制到设备上的d。然后将设备上的d复制到主机上的h1。为什么您认为主机h1在这个序列之后与主机h0不匹配?现在,关于您在注释中的最后一个问题,注释掉该行之所以会产生差异,是因为d到h的最后一个副本主机上的1也是异步的,因此下面的代码立即开始。主机上的d复制到h1和sumall之间存在竞争条件。显然,h0的sumall为复制到h1提供了足够的时间。感谢Robert!我弄糊涂了的是,如果这两个异步调用都将控制权返回到主机线程immedi当然,在主机上执行sumallh1时,由于赛车状况,h1不会从d获取所有信息,因此sumallh1不应该是99,因为尚未完成复制。