最近总听说GPU在计算上很厉害,现在脑子里冒出一个问题:有什么问题可以用CPU比GPU更快解决吗?
你能给我一些例子吗?
“GPU在计算方面非常强大!” 是的!
但 GPU 不是“更快的 CPU”。你可以这样总结:
我在这里将 GPU 视为GPGPU。由于图形管道,它们实际上有点不同,但想法是一样的!着色器就像我在这里解释的那样工作。
GPU 基本上是为计算“可并行化算法”而设计的。这意味着真正需要打开数百、数千甚至数百万个线程来解决某些确定的任务的算法!
一些非常愚蠢的例子:
不可并行化算法(CPU):
unsigned int X = 0;
unsigned int data[ 10000 ];
fillDataFromSomeWhereWithSomething( data );
for ( unsigned int i = 0; i < 10000; i++ )
{
X += data[ i ] * data[ i ];
}
对于循环的每个循环,X
取决于 的前一个值X
。所以我们为此打开线程是没有意义的!只有一个流!为此必须使用CPU!
可并行化算法(GPU):
int X[ 10000 ] = { 0 };
unsigned int data[ 10000 ];
fillDataFromSomeWhereWithSomething( data );
for ( unsigned int i = 0; i < 10000; i++ )
{
X[ i ] = data[ i ] * data[ i ];
}
在这里,对于 的每个值X[ ? ]
,结果都是直接依赖于的i
。周期是独立的!因此,不要循环超过 10000 个。您可以使用 GPU 为您打开 10000 个线程并并行执行所有循环。
这个怎么运作?
您将输入data
从 CPU 上传到 GPU。然后你告诉你的 GPU 在一个叫做 kernel 的小程序中执行 10000 个线程。每个内核都有自己的输出。内核将只计算:
X_i = data[ i ] * data[ i ];
其中X_i
(在 GPU 中)与X[ i ]
(在 CPU 上)相关。一旦 GPU 完成了所有 10000 个线程。结果被下载回 CPU,阵列X[]
被更新为所需的结果!
重要提示:当然,这只是一个虚拟示例。事实上,这个算法特别简单,以至于超过 10000 的循环对 CPU 来说真的没什么大不了的。向/从 GPU 上传/下载数据需要时间!所以一定值得!
想着这一切。你可以自己找出哪种算法更适合 CPU 或 GPU。当然,必须始终注意从 GPU 下载/上传数据的平衡。
GPU 通常与向 GPU 传输数据和从 GPU 传输数据的成本相关联。有一些问题可以说是几乎不涉及数据传输,但很多问题都是从必须传输到 GPU 的数据集开始的,并且总是有一些结果需要传输回来。
因此极小的问题在 GPU 上是不明智的:
int C = A + B;
如果这是您唯一需要处理 、 和 的工作C
,A
那么B
使用 GPU 可能不会有任何好处,因为传输数据的开销和成本超过了使用 GPU 的任何好处(并且没有任何好处)这种情况是因为在这个例子中没有内在的并行性)。
在大多数情况下,纯粹的顺序问题不会从使用 GPU 中受益,因为单个 GPU 线程执行代码的速度并不比现代 CPU 线程快。然而,在这里做出笼统的陈述更加困难,因为大多数纯顺序算法都可以以并行方式重铸,即使顺序算法在多个独立数据元素上运行时也可以天真地并行化。
示例“不可并行化”问题是非常可并行化的——尤其是如果你经常这样做的话。求和 10,000 个数字?首先并行求和 5000 对,然后并行求 2500 个结果,然后是随后的 1250 个结果,等等。 14 步,比任何单处理器都快。如果你有一个信息流,你可以在每个“fillDataFromSomeWhereWithSomething()”操作中使用 10,000 个并行加法操作。
如果您可以将数字预先分类到加号和减号箱中并添加相反和相似值的数字,则可以获得额外的奖励积分;这可以最大限度地减少累积的舍入误差,尽管它确实添加了 N log N 比较操作,并且比较与加法一样昂贵。OTOH,这意味着您可以使用更小、更快的加法器。