为什么cuda内核功能成本cpu?

时间:2015-06-09 08:52:38

标签: cuda

我发现在某些特殊情况下,cuda内核功能可以花费cpu

我正在开发win7(32位)+ vs2013(sp4)+ cuda 6.5 + GTX 650

我的代码如下:

cudaMalloc(args...); // malloc buffer outside the busy loop
while(1) {
    Sleep(1);
    kernel<<<B, T>>>(args...);
}

// and the kernel function(uses 30 registers) 
__global__ void kernel(args...) {
    char l_plain[256] = {0}; // use local variable
    // copy memory from argument buffer to l_plain
    // to avoid using global memory in the loop below

    // it costs CPU when the ROUND is very huge, like 1024
    for(int i=0; i<ROUND; i++) {
        uint u1, u2, u3, u4;
        md5_vfy(l_plain, 16, &u1, &u2, &u3, &u4); // a __device__ function that calculates md5 hash
        // prepare for next plain text
    }
}

我可以验证&lt;&lt;&lt;&lt; B T &gt;&gt;&gt;优化得很好,实际上我使用&lt;&lt;&lt; 32 128 &gt;&gt;&gt;,在Visual Profiler中占用率几乎为100%。

函数md5_vfy可以在这里找到:http://pastebin.com/KU3zUxpb

在我的机器上,当循环 ROUND 小于 720 时,cpu始终是空闲的,在任务管理器中花费0%。并且将循环 ROUND 更改为750/800/900/1000 ...... cpu成本也会线性增长。

我想知道是什么产生了影响,当 ROUND 非常大时,cpu的成本是多少?我可以提供Visual Profiler截图。

更新原因

我想知道这个,因为我想减少所有的cpu成本。我的程序应该支持两种模式:普通模式和游戏模式。普通模式需要100%的cpu和gpu,可能每秒运行800m散列。游戏模式需要0%cpu和5-xx%gpu,每秒运行50m哈希值。

感谢。

1 个答案:

答案 0 :(得分:2)

大概在增加ROUND时,内核执行时间会更长。

考虑你的主循环:

while(1) {
    Sleep(1);
    kernel<<<B, T>>>(args...);
}

似乎窗口Sleep()正在运行suspends thread execution for the specified period in milliseconds。因此,上述循环中的kernel是否需要多于或少于1毫秒才能执行,这将是程序动态行为的一个非常重要的因素。

假设执行时间小于1毫秒。在这种情况下,每次CPU线程从Sleep返回时,先前的内核调用就完成了,并且新的内核调用立即开始执行或多或少。由于内核调用是异步的,因此控制权返回到CPU线程,然后返回休眠状态,平均而言,该线程的CPU使用率非常低。

现在让我们考虑内核执行时间超过1毫秒的情况(可能是因为ROUND更大)。在这种情况下,您的主循环发出内核调用,进入休眠状态,1 ms后唤醒,但先前发出的内核仍在执行。没问题,CPU线程仍然可以发出另一个内核调用,这将进入一个等待队列。控制返回到CPU线程,然后CPU线程返回休眠状态。但是,随着时间的推移(并且相当快,1毫秒的睡眠时间),这个“多余”将加起来,最终队列将有两个待处理的启动,然后3个待处理的启动,等等。最后,待处理的启动队列将满满的。

这一点,行为发生了巨大变化。具有完整启动挂起队列的新内核启动不再异步,并且CPU线程将在此时阻塞,等待队列槽打开。此时,当线程忙 - 等待队列槽打开时,CPU利用率将达到100%。一旦队列槽打开,它将发出下一个内核调用(它将把它放在新打开的队列槽中),然后进入休眠状态。

这一点,随着您进一步增加内核执行的长度,CPU花费等待(100%利用率)与睡眠(0%利用率)的时间将按比例变化为你增加了内核执行的长度。

我不完全确定你的目标是什么,但为了避免这种情况,你可能会尝试将Sleep()句点增加到大致匹配(即稍微大于)你的内核执行时间,因为你增加{ {1}}值。