CUDA 程序不测量执行时间:cuda事件记录
CUDA program doesn't measure the execution time : cudaEventRecord
我需要真正了解 CUDA 如何工作来衡量时间执行。
让我们专注于这一部分(如果要测试它,整个代码位于消息的末尾)。
// Launching Kernel and measuring its time
cudaEventRecord(startComputation);
MatProd << <BlockPerGrid, ThreadPerBlock >> >(C, A, B, dimAx, dimBx, dimCx, dimCy);
cudaEventRecord(stopComputation);
//cudaEventSynchronize(stopComputation); // this line must be HERE and it returns me a good computation time.
cudaEventElapsedTime(&millisecondsPureComputation, startComputation, stopComputation);
cudaDeviceSynchronize(); // putting this doesn't do the job
//cudaEventSynchronize(stopComputation); // if I put it here instead it doesn't work.
std::cout << "Computation time : " << millisecondsPureComputation << "ms" << std::endl;
我如何理解这些事情。当程序运行时,CPU 会多次调用内核。cudaEventRecord、MatProd 和 cudaEventElapsedTime都在 GPU 上执行。
我的两个 cudaEventRecord 之间的时间是在我的 cudaEventElapsedTime 中计算的。
问题是:如果我的CPU在GPU计算方面太快,变量毫秒PureComputaion将保持它的初始值:0。
因此,在显示计算之前,我必须对CPU说"等待GPU已完成cudaEventElapsedTime"。这样,变量毫秒PureComputing将具有我们想要的值。
因此,在 cudaEventElapsedTime 之后放置一个 cudaDeviceSynchronise(); 应该就足够了。
但实际上当我这样做时它不起作用,变量仍然是 0。拥有非零数的唯一方法是将 cudaEvntSynchronize(stopComputing) 放在 cudaEventElapsedTime之前,我不明白为什么。
我的问题 :
为什么我放置cudaDeviceSynchronise()的方法不起作用?你能解释一下为什么在cudaEventElapsedTime工作之前放置cudaEventSynchronize(stopComputing)吗?它排他性的作用是什么?
#include <iostream>
#include <math.h>
#include <chrono>
__global__ void MatProd(float* C, float* A, float*B, int dimAx, int dimBx, int dimCx, int dimCy)
{
int row = blockDim.y*blockIdx.y + threadIdx.y;
int col = blockDim.x*blockIdx.x + threadIdx.x;
double Result = 0;
if (row <= dimCy - 1 && col <= dimCx - 1)
{
for (int k = 0; k < dimAx; k++)
{
Result += A[k + dimAx*row] * B[col + dimBx*k];
}
C[col + row*dimCx] = Result;
}
}
int main(void)
{
/* Initializing the inputs */
// Matrix sizes
int dimAx = 100;
int dimAy = 100;
int dimBx = 2;
int dimBy = dimAx;
int dimCx = dimBx;
int dimCy = dimAy;
// Matrix pointers
float *A, *B, *C;
// Variable to measure CUDA time execution.
float millisecondsPureComputation = 0;
cudaEvent_t startComputation, stopComputation;
cudaEventCreate(&startComputation);
cudaEventCreate(&stopComputation);
// Memory allocation
cudaMallocManaged(&A, dimAx*dimAy*sizeof(float));
cudaMallocManaged(&B, dimBx*dimBy*sizeof(float));
cudaMallocManaged(&C, dimCx*dimCy*sizeof(float));
// Initializing matrices
for (int i = 0; i < dimAy; i++)
{
for (int j = 0; j < dimAx; j++)
{
A[j + dimAx*i] = j + 10 * i;
}
}
for (int i = 0; i < dimBy; i++)
{
for (int j = 0; j < dimBx; j++)
{
B[j + dimBx*i] = (j + 1)*pow(i, 2);
}
}
// Kernel properties
int threadPerBlockx = 32;
int threadPerBlocky = 32;
int BlockPerGridx = 1 + (dimCx - 1) / threadPerBlockx;
int BlockPerGridy = 1 + (dimCy - 1) / threadPerBlockx;
dim3 BlockPerGrid(BlockPerGridx, BlockPerGridy, 1);
dim3 ThreadPerBlock(threadPerBlockx, threadPerBlocky, 1);
// Launching Kernel and measuring its time
cudaEventRecord(startComputation);
MatProd << <BlockPerGrid, ThreadPerBlock >> >(C, A, B, dimAx, dimBx, dimCx, dimCy);
cudaEventRecord(stopComputation);
//cudaEventSynchronize(stopComputation); // this line must be HERE and it returns me a good computation time.
cudaEventElapsedTime(&millisecondsPureComputation, startComputation, stopComputation);
cudaDeviceSynchronize(); // putting this doesn't do the job
//cudaEventSynchronize(stopComputation); // if I put it here instead it doesn't work.
std::cout << "Computation time : " << millisecondsPureComputation << "ms" << std::endl;
cudaFree(A);
cudaFree(B);
cudaFree(C);
return 0;
}
[编辑] 我更改了代码,现在它可以工作了,但我仍然不明白发生了什么。
cudaEventRecord(startComputation);
MatProd << <BlockPerGrid, ThreadPerBlock >> >(C, A, B, dimAx, dimBx, dimCx, dimCy);
//cudaDeviceSynchronize();
cudaEventRecord(stopComputation);
cudaDeviceSynchronize();
cudaEventElapsedTime(&millisecondsPureComputation, startComputation, stopComputation);
这是我的问题:
- 所以,cudaEventRecord(),cudaEventElapsedTime()在我的情况下在主机上执行,如果我理解得很好(文档中
__host__
的bc)。
在文档中,他们说cudaEventRecord捕获了蒸汽的内容。我并不完全清楚他们所说的流的"内容"是什么。
但是我不明白它是如何工作的。事实上,如果 MatProd 需要很长时间,CPU 将在 GPU 完成工作之前到达第二个 cudaEventRecord。所以我应该得到一个错误的结果...?
我这样说是因为您向我解释了这些 API 函数是在主机上执行的。因此,据我了解,它们将与内核并行启动。当我们在两个 cudaEventRecord() 之后同步时,我应该得到一个错误的结果......?
也许是因为我并没有真正理解您在主机上执行的意思,但我将其理解为在 CPU 上启动的功能(因此,它不需要等待内核完成)。
在设备上执行的唯一内容是前面带有__global__
或__device__
的代码。 其他所有内容,包括 CUDA 运行时 API 调用和实际的内核启动本身,都是主机代码。
您得到零,因为(第二个)事件尚未发生。
请阅读cudaEventElapsedTime
文档:
如果已对两个事件调用
cudaEventRecord()
但其中一个或两个事件尚未完成(即,cudaEventQuery()
将返回至少一个事件的cudaErrorNotReady
),则返回cudaErrorNotReady
。
这就是您的情况下正在发生的事情,并且由于您没有进行正确的 CUDA 错误检查,因此您对此视而不见。 当两个事件都未完成时(这意味着执行的 CUDA 流尚未到达两个事件),则cudaEventElapsedTime()
调用除了返回 CUDA 错误外不执行任何操作。
如果在cudaEventElapsedTime()
调用之前发出cudaDeviceSynchronize()
调用或适当的cudaEventSynchronize()
调用,这将强制 CPU 线程在该点等待,直到事件完成。 这将满足cudaEventElapsedTime()
调用的必要条件,并且您将获得经过时间的合理值。
添加进一步描述。 让我们一步一步地考虑一下。
- 在时间段 1 中,由于以下调用,CPU 代码将
startComputation
事件"记录"到 CUDA 执行流中:cudaEventRecord(startComputation);
CUDA 处理器 (GPU) 处于空闲状态。 因此,此时,特定的 CUDA 事件startComputation
被视为"已记录"但未"已完成"> - 在时间段 2 中,CPU 线程在上一个
cudaEventRecord
调用后向前移动到下一项,即内核启动:MatProd << <BlockPerGrid, ThreadPerBlock >> >(...)
。 在此期间,CPU 将内核启动作为要在 CUDA 执行流中处理的下一个项目。 由于上述时间段 1 中的活动,CUDA 处理器 (GPU) 有工作要做,因此它开始处理事件。 事件的这种处理将事件从"已记录"状态转换为"已完成"状态。 - 在时间段 3 中,CPU 线程在上一个内核启动后向前移动到下一项,这是另一个事件记录调用:
cudaEventRecord(stopComputation);
就像在时间段 1 中一样,这会将一个事件放入执行的 CUDA 流中,以便在内核执行完成后进行处理。 因此,此新事件处于"已录制"状态,而不是"已完成"状态。 在这段时间 3 中,GPU 开始执行内核并忙于执行内核。 - 在时间段 4 中,CPU 线程在上一个事件记录调用之后向前移动到下一项,这是对运行时 API 的请求,以便在两个事件 (
cudaEventElapsedTime
) 之间进行测量。 为了进行此测量,两个事件都必须处于"已完成"状态。 在这段时间 4 中,GPU 仍然忙于处理内核,因此它没有向前处理时间段 3 中"已记录"但未"完成"的stopComputation
事件。 因此,两个事件中的第一个(startComputation
)处于"已完成"状态,但两个事件中的第二个(stopComputation
)仍处于"已记录"状态。 因此,cuda 运行时 API 调用(如前所述)将返回错误,并且不会给出合理的测量结果。 它要求两个事件都处于"已完成"状态,然后才能返回请求的测量值。
那么,在经过的时间请求之前,您修改后的代码中有什么不同,并且可以在经过时间请求之前包含同步函数? 让我们在上面的时间段 3 结束后重新播放我们的时间线,因为到目前为止的所有内容都没有变化。 但是时间段 4 现在不同了:
在时间段 4 中,CPU 线程向前移动以处理 CUDA 事件记录调用后的下一项,但此指令是同步指令 (
cudaDeviceSynchronize()
)。 在这段时间 4 中,GPU 仍在忙于处理内核。 由于 CUDA 时间线/流仍有工作要做,因此 CPU 线程在同步步骤处停止。 它坐在那里等待。在时间段 5 中,GPU 仍在忙于处理内核。 CPU 线程在
cudaDeviceSynchronize()
调用时停滞不前。在时间段 6 中,GPU 仍在忙于处理内核。 CPU 线程在
cudaDeviceSynchronize()
调用时停滞不前。在时间段 7 中,GPU 完成内核的处理,并继续处理 CUDA 流中记录的下一项工作,即 cuda 事件
stopComputation
。 此事件的处理将stopComputation
的状态从"已记录"转换为"已完成"。 由于 GPU 在时间段 7 期间仍在执行某些操作,因此 CPU 线程在cudaDeviceSynchronize()
调用时会卡住等待。在时间段 8 中,GPU 已完成处理发给它的所有工作并返回到空闲状态。 因此,CPU 不再需要在
cudaDeviceSynchronize()
调用时等待,因此它移动到 CPU 线程中的下一项,即对经过时间测量的请求。 作为先前活动的结果,两个事件(startComputation
和stopComputation
)都处于"已完成"状态,因此事件经过时间测量请求是合法的,并且调用将返回合理的测量(并且没有错误)。
- 编译时未启用intel oneApi CUDA支持
- Android NDK传感器向事件队列报告奇怪的间隔
- 在cuda线程之间共享大量常量数据
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 从文本文件中读取时钟时间和事件时间并进行处理
- Cuda C++:设备上的Malloc类,并用来自主机的数据填充它
- WMI检测进程创建事件-c++
- EvtExportLogneneneba API正在将远程计算机的事件日志保存到远程PC本身.如何将其保存到主机
- CUDA内核和数学函数的显式命名空间
- 处理闪烁窗口事件
- C++Builder中的OnClick事件签名存在问题
- CUDA:统一内存和指针地址的更改
- 跟踪滚动条上的鼠标事件
- 什么是事件表 (wxWidgets)?
- 如何在 MFCaptureEngine 中获取"Camera removed"事件
- 给定顺序中的事件处理
- 当服务中的事件被触发时,如何将响应从服务发送回客户端?
- 调试 CUDA MMU 故障
- 在 C++/CLI 中将 .NET 事件从一个 DLL 引发到另一个 DLL
- CUDA 程序不测量执行时间:cuda事件记录