何时调用cudaDeviceSynchronize?

何时调用cudaDeviceSynchronize?,cuda,gpu,gpgpu,Cuda,Gpu,Gpgpu,何时真正需要调用cudaDeviceSynchronize函数 据我从CUDA文档中了解,CUDA内核是异步的,因此似乎我们应该在每次内核启动后调用cudaDeviceSynchronize。但是,我尝试了相同的代码(训练神经网络),包括和不包括任何cudaDeviceSynchronize,除了时间测量之前的代码。我发现我得到了相同的结果,但速度在7-12倍之间(取决于矩阵大小) 因此,问题是,是否有任何理由使用cudaDeviceSynchronize除时间测量 例如: 在使用cudaMe

何时真正需要调用
cudaDeviceSynchronize
函数

据我从CUDA文档中了解,CUDA内核是异步的,因此似乎我们应该在每次内核启动后调用
cudaDeviceSynchronize
。但是,我尝试了相同的代码(训练神经网络),包括和不包括任何
cudaDeviceSynchronize
,除了时间测量之前的代码。我发现我得到了相同的结果,但速度在7-12倍之间(取决于矩阵大小)

因此,问题是,是否有任何理由使用
cudaDeviceSynchronize
除时间测量

例如:

  • 在使用
    cudaMemcpy
    将数据从GPU复制回主机之前,是否需要此操作

  • 如果我做矩阵乘法,比如

    C = A * B
    D = C * F
    
我是否应该将
cudaDeviceSynchronize
放在两者之间

从我的实验来看,似乎我没有


为什么
cudaDeviceSynchronize
会使程序运行得如此缓慢?

使用
cudaDeviceSynchronize()
的一种情况是当您有多个
cudaStream
正在运行时,您希望让它们交换一些信息。一个真实的例子是量子蒙特卡罗模拟中的并行回火。在这种情况下,我们希望确保每个流在开始相互传递消息之前已经完成了一些指令集的运行并获得了一些结果,否则我们将最终传递垃圾信息。使用此命令的原因是
cudaDeviceSynchronize()
强制程序等待设备上所有流中以前发出的所有命令完成,然后再继续(参见《CUDA C编程指南》)。正如您所说,内核执行通常是异步的,因此当GPU设备执行内核时,CPU可以继续处理其他一些命令,向设备发出更多指令等,而不是等待。但是,当您使用此同步命令时,CPU将被强制空闲,直到所有GPU工作完成,然后再执行其他操作。此行为在调试时非常有用,因为由于设备代码的异步执行(无论是在一个流中还是在多个流中),您可能会在看似“随机”的时间发生segfault
cudaDeviceSynchronize()
将强制程序在继续之前确保流的内核/memcpys已完成,这样可以更容易地找出非法访问发生的位置(因为在同步过程中会出现故障)。

尽管CUDA内核启动是异步的,放置在一个流中的所有GPU相关任务(这是默认行为)都是按顺序执行的

那么比如说,

kernel1<<<X,Y>>>(...); // kernel start execution, CPU continues to next statement
kernel2<<<X,Y>>>(...); // kernel is placed in queue and will start after kernel1 finishes, CPU continues to next statement
cudaMemcpy(...); // CPU blocks until memory is copied, memory copy starts only after kernel2 finishes
kernel1(…);//内核开始执行,CPU继续执行下一个语句
内核2(…);//内核被放置在队列中,并将在内核1完成后启动,CPU继续执行下一个语句
cudaMemcpy(…);//CPU阻塞直到内存被复制,内存复制只有在内核2完成后才开始
因此,在您的示例中,不需要
cudaDeviceSynchronize
。但是,对于调试来说,检测哪个内核导致了错误(如果有)可能是有用的


cudaDeviceSynchronize
可能会导致一些速度减慢,但7-12x似乎太慢了。可能是时间测量有问题,或者内核速度非常快,显式同步的开销相对于实际计算时间来说是巨大的。

当您希望GPU开始处理某些数据时,通常会执行内核调用。 当您这样做时,您的设备(GPU)将开始执行您让它执行的任何操作。但是,与主机上的正常顺序程序不同,CPU将继续执行程序中的下一行代码。cudaDeviceSynchronize使主机(CPU)等待设备(GPU)执行完您启动的所有线程,因此您的程序将继续运行,就像它是一个正常的顺序程序一样


在小型简单程序中,当您使用GPU进行计算时,通常会使用cudaDeviceSynchronize,以避免请求结果的CPU与完成计算的GPU之间的时间不匹配。使用cudaDeviceSynchronize可以更轻松地编写程序,但有一个主要缺点:CPU一直处于空闲状态,而GPU进行计算。因此,在高性能计算中,您经常努力让您的CPU在等待GPU完成时进行计算。

nvcc并不总是持有“单一默认GPU流,除非另有规定”。我刚刚调试了一个程序,在这个程序中,我将一个内核上的冗长计算分解为一个分段计算,在for()循环中一次启动一个内核。连续的for()循环内核启动拾取上一个for()循环内核离开设备端的位置。错误在于nvcc编译器无法仅从主机代码中看到这一点,并试图同时启动每个内核。这意味着除了第一个内核外,所有的内核都在计算垃圾。@AleksandrDubinsky请仔细阅读我的评论。我非常明确地写下“nvcc并不总是坚持”。然后我给出了一个我用cuda gdb追踪到的特定bug的例子,这个例子正好证明了这一点。我绝对同意,根据Nvidia的文献,这不是CUDA应该如何工作的。。。但我所说的并不是一个观点:这是调试过程中对它在特定实例中的工作方式所做的观察。@opetrenko很抱歉不相信您的话,但您要么发现了一个非常关键的错误,要么代码中出现了其他问题。你有没有把问题发到SO上?@opetrenko NVCC不是主机代码的编译器。它是一个预处理器