CUDA(Compute Unified Device Architecture),显卡厂商NVidia推出的运算平台。
目前只有G80平台的NVidia显卡才能使用CUDA,工具集的核心是一个C语言编译器。G80中拥有128个单独的ALU,因此非常适合并行计算,而且数值计算的速度远远优于CPU
并行计算(Parallel Computing)是指同时使用多种计算资源解决计算问题的过程。为执行并行计算,计算资源应包括一台配有多处理机(并行处理)的计算机、一个与网络相连的计算机专有编号,或者两者结合使用。
从CUDA体系结构的组成来说,包含了三个部分:开发库、运行期环境和驱动
它是一个新的基础架构,这个架构可以使用GPU来解决商业、工业以及科学方面的复杂计算问题。它是一个完整的GPGPU解决方案,提供了硬件的直接访问接口,而不必像传统方式一样必须依赖图形API接口来实现GPU的访问。在架构上采用了一种全新的计算体系结构来使用GPU提供的硬件资源,从而给大规模的数据计算应用提供了一种比CPU更加强大的计算能力。CUDA采用C语言作为编程语言提供大量的高性能计算指令开发能力,使开发者能够在GPU的强大计算能力的基础上建立起一种效率更高的密集数据计算解决方案
nVidia CUDA 简介
GPGPU
在近年来,显示卡的运算单元(GPU)的速度越来越快,在某些方面的应用上,甚至已经大幅的超越 CPU 了!右图就大致说明了目前 GPU 和 CPU 的速度演进~很明显的可以看出来,单就浮点数的计算来说,GPU 的成长速度已经算是 CPU 的数倍了!(电晶体数目也大增、耗电量也大增… @@)
再加上 shader 程式的出现、演进,GPU 的可控制性也大幅增加!因此,将 GPU 用在非传统的 3D 图形显示方面的应用,也越来越多了…一般,会把这样的应用叫做 GPGPU(General-purpose computing on graphics processing units);而相关的应用,不管是数量和方向,都相当多了!有兴趣的人可以参考维基百科。
原则上,适用於 GPGPU 的问题,大多是用在可以把一个问题大量的拆解成多个相同、且彼此不相关的小问题的情况;在这种情况下,用 GPGPU 的方法,就可以把这些一样的小问题,丢给显示卡的 GPU 来大量平行化的处理,藉此达到加速的效果。
更 明确的说,由於 GPU 的架构设计,GPU 最适合拿来做的计算是「资料平行化的计算(data-parallel computations)」;也就是不同的资料,要用同样的程式来做计算。而在这种情况下,就可以透过把每一组资料的计算,都当成一个 thread 来计算,以此平行化来加速计算。其中,个别的计算量也要较高,如此才能避免平行话的负担大於平行化的加速。
传统的 GPGPU 的开发方法,都是透过 OpenGL 或 Direct3D 这一类现有的图形函式库,以编写 shading language 的方法,控制 shader 来想办法做到自己想要的计算;不过这样的缺点,就是必须要遵循著 Redner 的固定流程来进行。
CUDA
而 nVidia 所提出的 CUDA(Compute Unified Device Architecture) 也是一种 GPGPU 的技术;不过,透过 CUDA 来进行 GPGPU 的程式开发,是透过他的 C 语言的函式库和一些 CUDA 的延伸来编写,因此不用用到 OpenGL 或 Direct3D。以 nVidia 的官方说法来说,是说入门的门槛会相对的降低;此外也因为不用使用图形函式库,而不会被传统的 render pipeline 绑住使得在程式设计上更方便。
CUDA 的架构图大概如下:
其中,CUDA 大概分为 Library、runtime、Driver 三个部分;而我们在开发程式的时候,可以透过这三个部分,来使用 GPU 的计算能力。
而由於 CUDA 是个新的技术,目前只能用在 nVidia G80 核心的显示卡上,也就是 GeForce 8 系列,以及最新的 Quadro FX 了~不过,nVidia 也承诺现在用 CUDA 写的程式,在将来新的显示卡上也会可以正常运作。
Programming Model
在 CUDA 的程式架构里,程式执行的区域会分成两部分:
· Host
· Device
其中,「host」指的就是 CPU,而「device」就是 GPU 了~
在 CUDA 的程式架构中,主程式还是由 CPU 来执行;而当遇到了资料平行化处理的部分,就会将要在 GPU 跑的程式编译成 device 能执行的程式,再丢给 device 执行了。而这个程式在 CUDA 里把他叫做「kernel」。
而实际在运作时,CUDA 会产生许多在 device 上执行的 thread,每一个 thread 都会去执行 kernel 这个程式;虽然程式都是同一份,但是会因为其 index 的不同,而取得不同的资料来计算。
在 device 中要执行的 thread 中,要根据「最有效率的资料共用」来建置 thread 的 block;其中,thread block 的型式可以是一维、二维或三维的。而在同一个 block 里的 thread,有部分的记忆体(shared memory)是共用的;所以在校能的调整上,可能须要考虑到这一点。
而由於 thread block 的最大大小是有限制的,所以不能把所有的 thread 都塞到同一个 block 里(一般的 GPGPU 程式 thread 数目都会很多);这时候,可以用同样维度和大小的 thread block(当然,也要是同一个 kernel),来组成一个 grid 做批次处理。这个 grid 可以是一维或二维阵列的形式。
所以,实际上 CUDA 在执行一段 kernel 程式的时候,必须要指定他的 grid 和 block 的相关资讯(维度和大小);而会产生的 thread 数目,就会由这两者的资讯来决定。
Memory Model
在 CUDA 中,要让 thread 可以使用的变数,都必须要先把资料放置到 device 的记忆体里;而 device 的记忆体,又分为 DRAM 和 chip 上的记忆体两种。在实际使用上,分成下面几种记忆体的类型:
· registers
Read-write per-thread
· local memory
Read-write per-thread
· shared memory
Read-write per-block
· global memory
Read-write per-grid
· constant memory
Read-only per-grid
· texture memory
Read-only per-grid
可以发现,registers 和 local memory 是以 thread 为单位来使用的;而 shared memory 则是存在於 block,让每一个 thread 共用。上面这三种,都是在 chip 上的记忆体,相较之下,速度会比属於 DRAM 的记忆体来的快。
Grid 中的 global, constant, texture 这 三种 memory 可以让不同的 block 中的 thread 一起使用。此外,这三种记忆体则是属於 DRAM 上的记忆体,可以在同一个程式的不同 kernel 中,持续的存在、使用;而他们之间的差异,在於最佳化的方式不同。像 constant 和 texture 因为对於 thread 是唯独的,所以实际上会有快取的机制,可以用来加速读取。