【发布时间】:2020-09-21 22:57:53
【问题描述】:
我正在寻找一种方法来摆脱闲置代码中主机线程中的忙碌等待(不要复制该代码,它仅显示了我的问题的一个想法,它有许多基本错误):
cudaStream_t steams[S_N];
for (int i = 0; i < S_N; i++) {
cudaStreamCreate(streams[i]);
}
int sid = 0;
for (int d = 0; d < DATA_SIZE; d+=DATA_STEP) {
while (true) {
if (cudaStreamQuery(streams[sid])) == cudaSuccess) { //BUSY WAITING !!!!
cudaMemcpyAssync(d_data, h_data + d, DATA_STEP, cudaMemcpyHostToDevice, streams[sid]);
kernel<<<gridDim, blockDim, smSize streams[sid]>>>(d_data, DATA_STEP);
break;
}
sid = ++sid % S_N;
}
}
有没有办法让主机线程空闲并以某种方式等待某个流完成,然后准备并运行另一个流?
编辑:我在代码中添加了 while(true),以强调忙等待。现在我执行所有流,并检查它们中的哪一个完成了运行另一个新流。 cudaStreamSynchronize 等待特定流完成,但我想等待任何作为第一个完成工作的流。
EDIT2:我以休闲的方式摆脱了忙碌等待:
cudaStream_t steams[S_N];
for (int i = 0; i < S_N; i++) {
cudaStreamCreate(streams[i]);
}
int sid = 0;
for (int d = 0; d < DATA_SIZE; d+=DATA_STEP) {
cudaMemcpyAssync(d_data, h_data + d, DATA_STEP, cudaMemcpyHostToDevice, streams[sid]);
kernel<<<gridDim, blockDim, smSize streams[sid]>>>(d_data, DATA_STEP);
sid = ++sid % S_N;
}
for (int i = 0; i < S_N; i++) {
cudaStreamSynchronize(streams[i]);
cudaStreamDestroy(streams[i]);
}
但它似乎比在主机线程上忙等待的版本慢一点。我认为这是因为,现在我在流上静态分配作业,所以当一个流完成工作时,它处于空闲状态,直到每个流完成工作。之前的版本动态将工作分配到第一个空闲流,效率更高,但是宿主线程有忙等待。
【问题讨论】:
-
我不认为上面的代码做你想做的事。它确实不在流 1 开始之前等待流 0 完成。相反,它确保流 0 中的任何先前启动在流 0 上启动更多作业之前完成(这是不必要的,因为这已经是流的工作方式)。要使您的代码按照您的要求执行,您需要 cudaThreadSynchronize()、cudaStreamSynchronize(0) 或 cudaStreamSynchronize(streams[sid-1])。
-
没有。你的编辑没有按照你说的去做,而且你不了解流是如何工作的。在第一个示例中,no 等待 - 您的 cudaStreamQueries always 返回 true,因为您调用 cudaStreamQuery(x) before 您将启动任何内容放在流中x。在新示例中,您在同步之前调用内核。它比较慢,因为同步实际上必须等待你的 memcpy/kernel 完成。
-
是的,您的权利最终开始返回 true,因为您在旧流上安排了新作业。关键是,没有必要这样做!当您在一个流上安排两个作业时,第二个将等待第一个完成后再开始。您不必管理它,设备可以!您可以连续启动它们。 (请参阅下面我过于详细的答案)
-
是的,我知道我可以做到,但是当内核的执行时间取决于给定的数据时怎么办。当我通过流分配作业时,可能会发生其中许多变得空闲的情况,因为一个流获得了所有持续时间很长的作业。
-
嗯。现在我明白了……DATA_SIZE 有多大?您是否有机会将每个内核放入它自己的流中?
标签: cuda cuda-streams busy-loop