以下代码用于测试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_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;
}
Run Code Online (Sandbox Code Playgroud)
h0/h1的打印输出表明cudaMemcpyAsync与主机同步
sum h0: 99
sum h1: 99
Run Code Online (Sandbox Code Playgroud)
但是,包含cudaMemcpyAsync调用的时差表明它们与主机不同步
cuda H->D 100000000 takes: 0 ms
cuda D->H 100000000 takes: 0 ms
Run Code Online (Sandbox Code Playgroud)
因为cuda-profiling结果不支持这个:
method=[ memcpyHtoDasync ] gputime=[ 154896.734 ] cputime=[ 17.000 ]
method=[ memcpyDtoHasync ] gputime=[ 141175.578 ] cputime=[ 6.000 ]
Run Code Online (Sandbox Code Playgroud)
不知道为什么......
这里有(至少)两件事情.
你的第一个观察是:
sum h0: 99
sum h1: 99
Run Code Online (Sandbox Code Playgroud)
发出给同一流的 CUDA调用将按顺序执行.如果您希望CUDA调用彼此重叠,则必须将它们发布到单独的流中.由于您要向设备发出cuda memcpy,并且在同一个流中从设备发出cuda memcpy,它们将按顺序执行.第二个将不会开始,直到第一个完成(即使两个都立即排队).因此数据是完整的(在第一个cudaMemcpy之后),并且您观察到两个数组都生成了正确的总和.
您剩下的观察结果也是一致的.您报告的是:
cuda H->D 100000000 takes: 0 ms
cuda D->H 100000000 takes: 0 ms
Run Code Online (Sandbox Code Playgroud)
这是因为这两个异步调用都立即将控制权返回给主机线程,并且调用排队等待异步执行以执行主机.然后,呼叫与进一步的主机执行并行进行.由于控制立即返回给主机,并且您使用基于主机的计时方法对操作进行计时,它们似乎没有时间.
当然,它们实际上并不是零时间,而您的分析器结果表明了这一点.由于GPU与CPU异步执行(在本例中包括cudaMemcpyAsync),因此探查器显示cudaMemcpy操作所用的实际"实际"时间,gputime
以及cpu上的"明显时间",即时间量启动操作所需的cpu报告为cputime
.请注意,与gputime相比,cputime非常小,即它几乎是瞬时的,因此基于主机的计时方法报告零时间.但它们实际上并不是零时间完成,而分析器报告说.
如果您使用了cudaEvent计时方法,您当然会看到不同的结果,这将更接近您的探查器gputime
结果.
归档时间: |
|
查看次数: |
645 次 |
最近记录: |