kli*_*ijo 2 gpgpu simd cpu-architecture
GPGPU编程是否只允许执行SIMD指令?如果是这样,重新编写一个设计为在通用CPU上运行以在GPU上运行的算法必定是一项繁琐的工作?算法中是否还有一种模式可以转换为SIMD架构?
嗯,GPGPU仅支持SIMD执行并不完全准确.许多GPU都有一些非SIMD组件.但是,总的来说,要充分利用GPU,您需要运行SIMD代码.
但是,您不一定要编写SIMD指令.即GPU SIMD是不一样的CPU SIMD -即不一样写代码采取的x86 SSE(流SIMD扩展),等等.实际上的优势,因为谁布拉夫CPU SIMD你(我大量参与其中的人在英特尔MMX中,最早的一个,并且已经跟随FP SIMD的发展)我经常感到有必要纠正那些说像英特尔这样的CPU有SIMD指令的人.我更喜欢将它们视为打包向量指令,尽管我勉强称它们为SIMD打包向量指令集,因为每个人都误用了这个名字.我还强调CPU SIMD指令集如MMX和SSE可能有SIMD打包向量执行单元 - 整数和浮点ALU等 - 但它们没有SIMD控制流,并且它们通常没有SIMD存储器访问(又称分散/聚集(尽管英特尔Larrabee正在向这个方向发展)).
在我这个comp-arch.net维基有些(我写的计算机体系结构我的爱好): - http://semipublic.comp-arch.net/wiki/SIMD - HTTP://semipublic.comp-arch. net/wiki/SIMD_packed_vector - http://semipublic.comp-arch.net/wiki/Difference_between_vector_and_packed_vector - http://semipublic.comp-arch.net/wiki/Single_Instruction_Multiple_Threads_(SIMT) 虽然我为尚未编写页面而道歉讨论SIMD打包矢量指令序列,如英特尔MMX或SIMD.
但我不指望你阅读以上所有内容.让我试着解释一下.
想象一下,当您以简单,标量的方式编写时,您有一段看起来像这样的代码:
// operating on an array with one million 32b floating point elements A[1000000]
for i from 0 upto 999999 do
if some_condition(A[i]) then
A[i] = function1(A[i])
else
A[i] = function2(A[i])
Run Code Online (Sandbox Code Playgroud)
其中function1()和function2()非常简单,可以内联 - 比如function1(x)= x*x和function2(x)= sqrt(x).
在CPU上.要使用类似SSE的东西,你必须(1)将数组分成块,比如说256位AVX的大小,(2)自己处理IF语句,使用掩码等.就像是:
for i from 0 upto 999999 by 8 do
register tmp256b_1 = load256b(&A[i])
register tmp256b_2 = tmp256b_1 * tmp256b_1
register tmp256b_3 = _mm_sqrt_ps(tmp256b_1) // this is an "intrinsic"
// a function, possibly inlined
// doing a Newton Raphson to evaluate sqrt.
register mask256b = ... code that arranges for you to have 32 1s in the "lane"
where some_condition is true, and 0s elsewhere...
register tmp256b_4 = (tmp256b_1 & mask) | (tmp256b_3 | ~mask);
store256b(&A[i],tmp256b_4)
Run Code Online (Sandbox Code Playgroud)
你可能认为这不是很糟糕,但请记住,这是一个简单的例子.想象一下多个嵌套的IF,依此类推.或者,假设"some_condition"是块状的,因此您可以通过跳过所有function1或所有function2的部分来节省大量不必要的计算...
for i from 0 upto 999999 by 8 do
register mask256b = ... code that arranges for you to have 32 1s in the "lane"
where some_condition is true, and 0s elsewhere...
register tmp256b_1 = load256b(A[i])
if mask256b == ~0 then
register tmp256b_2 = tmp256b_1 * tmp256b_1
store256b(&A[i],tmp256b_2)
else mask256b == 0 then
register tmp256b_3 = _mm_sqrt_ps(tmp256b_1) // this is an "intrinsic"
store256b(&A[i],tmp256b_3)
else
register tmp256b_1 = load256b(&A[i])
register tmp256b_2 = tmp256b_1 * tmp256b_1
register tmp256b_3 = _mm_sqrt_ps(tmp256b_1)
register tmp256b_4 = (tmp256b_1 & mask) | (tmp256b_3 | ~mask);
store256b(&A[i],tmp256b_4)
Run Code Online (Sandbox Code Playgroud)
我想你可以拍照吗?当你有多个数组时,它会变得更加复杂,有时候数据会在256位边界上对齐,有时则不会(例如,在模板计算中,你在所有对齐上运行).
现在,这里大概是像GPU这样的东西:
// operating on an array with one million 32b floating point elements A[1000000]
for all i from 0 upto 999999 do
if some_condition(A) then
A = function1(A)
else
A = function2(A)
Run Code Online (Sandbox Code Playgroud)
这看起来不像原始的标量代码吗?唯一真正的区别是你丢失了数组索引A [i].(实际上,一些GPGPU语言保留了数组索引,但我所知道的大多数都没有.)
现在,我遗漏了(a)Open/CL的C语法,(b)将Open/CL代码连接到C或C++代码所需的所有设置(有比CUDA或OpenCL更好的语言) - 这些都有很多不妥之处.但它们可以在CPU和GPU上使用很多地方[**].但我想我已经提出了问题的核心:
关于GPGPU计算的关键是你编写SIMD,数据并行冷.但是你写的比你编写CPU风格的SSE代码更高.甚至比编译器内在函数更高级别.
首先,GPGPU编译器(例如OpenCL或CUDA编译器)处理背后的大量数据管理.编译器安排执行控制流程,IF语句等.
请注意,正如我用[**]标记的那样,有时候所谓的SIMD GPGPU编译器可以生成将在CPU和GPU上运行的代码.即SIMD编译器可以生成使用CPU SIMD指令集的代码.
但GPU本身具有特殊的硬件支持,运行此SIMD代码,经过适当编译,比使用CPU SIMD指令在CPU上运行的速度快得多.最重要的是,GPU有更多的执行单元 - 例如像AMD Bulldoser这样的CPU有2套128位宽的FMACS,即每个周期能够做8个FMAC.计算芯片上的CPU数量 - 比如8 - 可以为每个周期提供64个CPU.而现代GPU每个周期可能有2,048个32b FMAC.即使以1/2或1/4的时钟速率运行,这也是一个很大的不同.
GPU如何拥有更多硬件?嗯,首先,它们通常比CPU更大.但是,他们也倾向于不花费(有人说"浪费")硬件来处理大型缓存和CPU花费的无序执行.CPU试图快速进行一次或几次计算,而GPU并行进行多次计算,但单独比CPU慢.尽管如此,GPU每秒可以执行的计算总数远远高于CPU可以执行的计算.
FGPU具有其他硬件优化.例如,它们运行的线程比CPU多得多.英特尔CPU每个CPU有2个超线程,而在8个CPU核心芯片上有16个线程,而GPU可能有数百个.等等.
作为计算机架构师,我最感兴趣的是,许多GPU都具有SIMD控制流的特殊硬件支持.与运行SSE的CPU相比,它们可以更有效地操作这些掩码.
等等.
无论如何,我希望我已经说明了我的观点
当你做必须编写代码SIMD向GPGPU系统(如OpenCL的)上运行.
您不应该将此类SIMD与您必须编写的SIMD代码混淆,以利用英特尔SSE.
它更清洁.
越来越多的编译器允许相同的代码在DCPU和GPU上运行.即他们越来越多地支持干净的"真正的SIMD"编码风格,而不是现在必须利用MMX和SSE以及AVX所需的伪"伪SIMD"编码风格.这很好 - 这样的代码在CPU和GPU上编程同样"不错".但GPU经常以更快的速度运行它.英特尔发表了一篇名为"揭开100X GPU与CPU神话的论述:对CPU和GPU吞吐量计算的评估",http://www.hwsw.hu/kepek/hirek/2010/06/p451-lee.pdf .它表示GPU平均"仅"快2.5倍.但这是经过大量积极优化之后的事情.GPU代码通常更容易编写.而且我不了解你,但我觉得"只有"2.5倍的速度并没有那么多打喷嚏.特别是因为GPGPU代码通常更容易阅读.
现在,没有免费的午餐.如果您的代码自然是数据并行的,那就很好.但有些同志不是.这可能是一种痛苦.
而且,与所有机器一样,GPU也有它们的怪癖.
但是,如果您的代码自然是数据并行的,那么您可以获得极高的加速,代码更具可读性.
我是一名CPU设计师.我希望从GPU中借用很多想法,让男性CPU运行得更快,反之亦然.
| 归档时间: |
|
| 查看次数: |
417 次 |
| 最近记录: |