使用异步内存传输的CUDA CPU-GPU回调

使用异步内存传输的CUDA CPU-GPU回调,cuda,Cuda,各位Cuda程序员 我正在尝试使用轮询机制实现cpu gpu回调机制。我有两个长度为1的数组(a和cpuflag,对应于设备端dev_a和gpuflag)(基本上是2个变量) 第一个CPU清除并等待gpuflag的更新。GPU看到此清除,然后更新gpuflag。CPU异步地将gpuflag传输到cpuflag,并等待标志中的更新。一旦CPU看到更新,它会再次重置a并将其异步发送到gpu。GPU再次看到了a的清除并更新了gpuflag,乒乓过程继续进行。我希望这个过程持续100次 全部代码都在这里

各位Cuda程序员

我正在尝试使用轮询机制实现cpu gpu回调机制。我有两个长度为1的数组(a和cpuflag,对应于设备端dev_a和gpuflag)(基本上是2个变量)

第一个CPU清除并等待gpuflag的更新。GPU看到此清除,然后更新gpuflag。CPU异步地将gpuflag传输到cpuflag,并等待标志中的更新。一旦CPU看到更新,它会再次重置a并将其异步发送到gpu。GPU再次看到了a的清除并更新了gpuflag,乒乓过程继续进行。我希望这个过程持续100次

全部代码都在这里。只需说出nvcc-o output filename.cu即可编译它 我无法理解为什么代码没有显示乒乓球行为。非常感谢您的任何帮助。提前谢谢

#include <stdio.h>

#define LEN 1
#define MAX 100

__global__ void myKernel(int len, int *dev_a, int *gpuflag) {
        int tid = threadIdx.x;
        gpuflag[tid] = 0;

        while(true){
        //Check if cpu has completed work
                if(dev_a[tid] == 0){
            //Do gpu work and increment flag
                        dev_a[tid] = 1;
                        gpuflag[tid]++;

            //Wait till cpu detects the flag increment and resets
                        while(true){
                                if(dev_a[tid] == 0){
                                        break;
                                }
                        }
                }
        //Max 100 ping pongs
        if(gpuflag[tid]==MAX){
            break;
        }
        }
}

int main( void ) {
        int index, *cpuflag, *gpuflag, value;

        int *a;
        int *dev_a;

        cudaStream_t stream0, stream1;

        cudaStreamCreate( &stream0 );
        cudaStreamCreate( &stream1 );

        cudaMalloc ( (void**)&gpuflag, LEN*sizeof(int) );
        cudaMemset ( gpuflag, 0, LEN*sizeof(int) );
        cudaHostAlloc( (void**)&cpuflag, LEN*sizeof(int), cudaHostAllocDefault );

        cudaMalloc ( (void**)&dev_a, LEN*sizeof(int) );
        cudaMemset ( dev_a, 0, LEN*sizeof(int) );
        cudaHostAlloc( (void**)&a, LEN*sizeof(int), cudaHostAllocDefault );

    //Reset everything
        for(int i=0; i<LEN; i++)
                a[i] = 0;

    //Auxillary variables
        index = 0;
    value = 1;

    //call kernel
        myKernel<<<1,1,0,stream0>>>(LEN, dev_a, gpuflag);

        while(true){
        //Asynchronously copy gpu flag
                cudaMemcpyAsync(cpuflag, gpuflag, LEN*sizeof(int), cudaMemcpyDeviceToHost, stream1);
        //Check if increment has happened or not
                if(cpuflag[index] == value){
            //if yes, reset 
                for(int i=0; i<LEN; i++)
                        a[i] = 0;
            //transfer asynchronously
                    cudaMemcpyAsync(dev_a, a, LEN*sizeof(int), cudaMemcpyHostToDevice, stream1);
            //increment pattern
            value++;
                        printf("GPU updated once. Value is a[%d] = %d, cpuflag = %d\n", index, a[index], cpuflag[index]);
                } else {
                        printf("------------GPU didn't updated. Value is a[%d] = %d, cpuflag = %d\n", index, a[index], cpuflag[index]);
        }

        //Max 100 ping-pongs
        if(value == MAX){
            break;
        }
        }

    cudaFreeHost(a);
    cudaFreeHost(cpuflag);

    cudaFree(dev_a);
    cudaFree(gpuflag);

    cudaStreamDestroy( stream0 );
    cudaStreamDestroy( stream1 );

        return 0;
}
#包括
#定义len1
#定义最大值100
__全局无效myKernel(int len,int*dev_a,int*gpuflag){
int tid=threadIdx.x;
gpuflag[tid]=0;
while(true){
//检查cpu是否已完成工作
if(dev_a[tid]==0){
//gpu是否工作和增量标志
开发署[tid]=1;
gpuflag[tid]++;
//等待cpu检测到标志增量并重置
while(true){
if(dev_a[tid]==0){
打破
}
}
}
//最多100个乒乓球
如果(gpuflag[tid]==最大值){
打破
}
}
}
内部主(空){
int索引,*cpuflag,*gpuflag,值;
int*a;
国际开发署;
cudaStream_t stream0,stream1;
cudaStreamCreate(&stream0);
cudaStreamCreate(&stream1);
cudamaloc((void**)和gpuflag,LEN*sizeof(int));
cudaMemset(gpuflag,0,LEN*sizeof(int));
cudaHostAlloc((void**)和cpuflag,LEN*sizeof(int),cudaHostAllocDefault);
cudamaloc((void**)和dev_a,LEN*sizeof(int));
cudaMemset(dev_a,0,LEN*sizeof(int));
cudaHostAlloc((无效**)和a,LEN*sizeof(int),cudaHostAllocDefault);
//重置一切

对于(int i=0;i而言,可能缺少的主要内容是适当使用
volatile

下面是一个简化的、充分发挥作用的示例:

$ cat t763.cu
#include <stdio.h>

#define LEN 1
#define MAX 100
#define DLEN 1000
#define nTPB 256

#ifdef CDP_WORKER
__global__ void cdp_worker(int len, float *data){

  int tid = threadIdx.x+blockDim.x*blockIdx.x;
  if (tid < len) data[tid]++; // simple increment
}
#endif

// only call this kernel with 1 thread
__global__ void myKernel(int len, int dlen, volatile int *dev_a, int *gpuflag, float *data) {
        int tid = threadIdx.x+blockDim.x*blockIdx.x;

        while(gpuflag[tid] < MAX){
        //Check if cpu has completed work
                if(dev_a[tid] == 0){
            //Do gpu work and increment flag
#ifdef CDP_WORKER
                        cdp_worker<<<(dlen+nTPB-1)/nTPB, nTPB>>>(dlen, data);
                        cudaDeviceSynchronize();
#endif
                        dev_a[tid] = 1;
                        gpuflag[tid]++;

                                }
        }
}

void issue_work(int value, float *h_data, float *d_data, int len, cudaStream_t mystream){
#ifdef CDP_WORKER
  cudaMemcpyAsync(h_data, d_data, len*sizeof(float), cudaMemcpyDeviceToHost, mystream);
  cudaStreamSynchronize(mystream);
  for (int i = 0; i < len; i++) if (h_data[i] != value+1) {printf("fault - was %f, should be %f\n", h_data[i], (float)(value+1)); break;}
  cudaMemcpyAsync(d_data, h_data, len*sizeof(float), cudaMemcpyHostToDevice, mystream); // technically not really necessary
  cudaStreamSynchronize(mystream);
#endif
  return;
}
int main( void ) {
        int *gpuflag, value;
        float *h_data, *d_data;
        cudaHostAlloc(&h_data, DLEN*sizeof(float), cudaHostAllocDefault);
        cudaMalloc(&d_data, DLEN*sizeof(float));
        volatile int *z_a;

        cudaStream_t stream0, stream1;

        cudaStreamCreate( &stream0 );
        cudaStreamCreate( &stream1 );

        cudaMalloc ( (void**)&gpuflag, LEN*sizeof(int) );
        cudaMemset ( gpuflag, 0, LEN*sizeof(int) );
        cudaMemset ( d_data, 0, DLEN*sizeof(float));
        cudaHostAlloc( (void**)&z_a, LEN*sizeof(int), cudaHostAllocMapped );
        for (int i = 0; i < LEN; i++) z_a[i] =
        value = 0;
    //call kernel
        myKernel<<<1,1,0,stream0>>>(LEN, DLEN, z_a, gpuflag, d_data);

        while(value<MAX){
          if (z_a[0] == 1) {
             issue_work(value, h_data, d_data, DLEN, stream1);
             z_a[0] = 0;
             printf("%d", value%10);
             value++;}
        }
        printf("\n");
        return 0;
}
$ nvcc -o t763 t763.cu
$ cuda-memcheck ./t763
========= CUDA-MEMCHECK
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
========= ERROR SUMMARY: 0 errors
$ nvcc -DCDP_WORKER -arch=sm_35 -rdc=true t763.cu -o t763 -lcudadevrt
$ cuda-memcheck ./t763
========= CUDA-MEMCHECK
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
========= ERROR SUMMARY: 0 errors
$
$cat t763.cu
#包括
#定义len1
#定义最大值100
#定义DLEN 1000
#定义nTPB 256
#ifdef CDP_工人
__全局无效cdp工人(整数、浮点*数据){
int tid=threadIdx.x+blockDim.x*blockIdx.x;
if(tid虽然(值CUDA内存模型不能保证这种内存一致性。如果您的GPU支持,您可能可以通过让内核执行系统范围的线程围栏来取得更大的进步,但这确实不是定义的行为,我同意,这导致了未定义的行为。但在CUDA 5.0中,您可以使用主机挂钩机制将主机排队在特定内核完成执行后调用。它在dev.preview 5.0中是完全异步的。