没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
1
深入浅出谈 CUDA
Hotball
CUDA
是
NVIDIA
的
GPGPU
模型,它使用
C
语言为基础,可以直接以大多数人熟悉的
C
语言,写出在显示芯片上执行的程序,而不需要去学习特定的显示芯片的指令或是特殊的结
构。
”
CUDA是什么?能吃吗?
编者注:
NVIDIA
的
GeFoce 8800GTX
发布后,它的通用计算架构
CUDA
经过一年多的推广后,
现在已经在有相当多的论文发表,在商业应用软件等方面也初步出现了视频编解码、金融、
地质勘探、科学计算等领域的产品,是时候让我们对其作更深一步的了解。为了让大家更容
易了解
CUDA
,我们征得
Hotball
的本人同意,发表他最近亲自撰写的本文。这篇文章的特点是
深入浅出,也包含了
hotball
本人编写一些简单
CUDA
程序的亲身体验,对于希望了解
CUDA
的
读者来说是非常不错的入门文章,
PCINLIFE
对本文的发表没有作任何的删减,主要是把一些
台湾的词汇转换成大陆的词汇以及作了若干
"
编者注
"
的注释。
现代的显示芯片已经具有高度的可程序化能力,由于显示芯片通常具有相当高的内存带宽,
以及大量的执行单元,因此开始有利用显示芯片来帮助进行一些计算工作的想法,即 GPGPU。
CUDA 即是 NVIDIA 的 GPGPU 模型。
NVIDIA 的新一代显示芯片,包括 GeForce 8 系列及更新的显示芯片都支持 CUDA。NVIDIA
免费提供 CUDA 的开发工具(包括 Windows 版本和 Linux 版本)、程序范例、文件等等,
可以在
CUDA Zone 下载。
GPGPU 的优缺点
使用显示芯片来进行运算工作,和使用 CPU 相比,主要有几个好处:
1. 显示芯片通常具有更大的内存带宽。例如,NVIDIA 的 GeForce 8800GTX 具有超过
50GB/s 的内存带宽,而目前高阶 CPU 的内存带宽则在 10GB/s 左右。
2. 显示芯片具有更大量的执行单元。例如 GeForce 8800GTX 具有 128 个 "stream
processors",频率为 1.35GHz。CPU 频率通常较高,但是执行单元的数目则要少得多。
3. 和高阶 CPU 相比,显卡的价格较为低廉。例如目前一张 GeForce 8800GT 包括
512MB 内存的价格,和一颗 2.4GHz 四核心 CPU 的价格相若。
当然,使用显示芯片也有它的一些缺点:
1. 显示芯片的运算单元数量很多,因此对于不能高度并行化的工作,所能带来的帮助就
不大。
2. 显示芯片目前通常只支持 32 bits 浮点数,且多半不能完全支持 IEEE 754 规格, 有
些运算的精确度可能较低。目前许多显示芯片并没有分开的整数运算单元,因此整数
运算的效率较差。
3. 显示芯片通常不具有分支预测等复杂的流程控制单元,因此对于具有高度分支的程序,
效率会比较差。
4. 目前 GPGPU 的程序模型仍不成熟,也还没有公认的标准。例如 NVIDIA 和
AMD/ATI 就有各自不同的程序模型。
整体来说,显示芯片的性质类似 stream processor,适合一次进行大量相同的工作。CPU 则比
较有弹性,能同时进行变化较多的工作。
CUDA 架构
CUDA 是 NVIDIA 的 GPGPU 模型,它使用 C 语言为基础,可以直接以大多数人熟悉的 C
语言,写出在显示芯片上执行的程序,而不需要去学习特定的显示芯片的指令或是特殊的结
构。
在 CUDA 的架构下,一个程序分为两个部份:host 端和 device 端。Host 端是指在 CPU 上
执行的部份,而 device 端则是在显示芯片上执行的部份。Device 端的程序又称为 "kernel"。
通常 host 端程序会将数据准备好后,复制到显卡的内存中,再由显示芯片执行 device 端程
序,完成后再由 host 端程序将结果从显卡的内存中取回。
由于 CPU 存取显卡内存时只能透过 PCI Express 接口,因此速度较慢(PCI Express x16 的
理论带宽是双向各 4GB/s),因此不能太常进行这类动作,以免降低效率。
在 CUDA 架构下,显示芯片执行时的最小单位是 thread。数个 thread 可以组成一个 block。
一个 block 中的 thread 能存取同一块共享的内存,而且可以快速进行同步的动作。
每一个 block 所能包含的 thread 数目是有限的。不过,执行相同程序的 block,可以组成
grid。不同 block 中的 thread 无法存取同一个共享的内存,因此无法直接互通或进行同步。
因此,不同 block 中的 thread 能合作的程度是比较低的。不过,利用这个模式,可以让程序
不用担心显示芯片实际上能同时执行的 thread 数目限制。例如,一个具有很少量执行单元的
显示芯片,可能会把各个 block 中的 thread 顺序执行,而非同时执行。不同的 grid 则可以
执行不同的程序(即 kernel)。
Grid、block 和 thread 的关系,如下图所示:
2
PCI Express
x16接口(4GB/s)
3
每个 thread 都有自己的一份 register 和 local memory 的空间。同一个 block 中的每个
thread 则有共享的一份 share memory。此外,所有的 thread(包括不同 block 的 thread)都
共享一份 global memory、constant memory、和 texture memory。不同的 grid 则有各自的 global
memory、constant memory 和 texture memory。这些不同的内存的差别,会在之后讨论。
执行模式
由于显示芯片大量并行计算的特性,它处理一些问题的方式,和一般 CPU 是不同的。主要的
特点包括:
1. 内存存取 latency 的问题:CPU 通常使用 cache 来减少存取主内存的次数,以避免
内存 latency 影响到执行效率。显示芯片则多半没有 cache(或很小),而利用并行化
执行的方式来隐藏内存的 latency(即,当第一个 thread 需要等待内存读取结果时,
则开始执行第二个 thread,依此类推)。
2. 分支指令的问题:CPU 通常利用分支预测等方式来减少分支指令造成的 pipeline
bubble。显示芯片则多半使用类似处理内存 latency 的方式。不过,通常显示芯片处
理分支的效率会比较差。
因此,最适合利用 CUDA 处理的问题,是可以大量并行化的问题,才能有效隐藏内存的
latency,并有效利用显示芯片上的大量执行单元。使用 CUDA 时,同时有上千个 thread 在
执行是很正常的。因此,如果不能大量并行化的问题,使用 CUDA 就没办法达到最好的效率
了。
CUDA Toolkit的安装
目前 NVIDIA 提供的 CUDA Toolkit(可从
这里下载)支持 Windows (32 bits 及 64 bits 版
本)及许多不同的 Linux 版本。
CUDA Toolkit 需要配合 C/C++ compiler。在 Windows 下,目前只支持 Visual Studio 7.x 及
Visual Studio 8(包括免费的 Visual Studio C++ 2005 Express)。Visual Studio 6 和 gcc 在
Windows 下是不支援的。在 Linux 下则只支援 gcc。
这里简单介绍一下在 Windows 下设定并使用 CUDA 的方式。
下载及安装
在 Windows 下,CUDA Toolkit 和 CUDA SDK 都是由安装程序的形式安装的。CUDA Toolkit
包括 CUDA 的基本工具,而 CUDA SDK 则包括许多范例程序以及链接库。基本上要写
CUDA 的程序,只需要安装 CUDA Toolkit 即可。不过 CUDA SDK 仍值得安装,因为里面
的许多范例程序和链接库都相当有用。
CUDA Toolkit 安装完后,预设会安装在 C:\CUDA 目录里。其中包括几个目录:
• bin -- 工具程序及动态链接库
• doc -- 文件
• include -- header 檔
• lib -- 链接库档案
• open64 -- 基于 Open64 的 CUDA compiler
• src -- 一些原始码
安装程序也会设定一些环境变量,包括:
• CUDA_BIN_PATH -- 工具程序的目录,默认为 C:\CUDA\bin
• CUDA_INC_PATH -- header 文件的目录,默认为 C:\CUDA\inc
注意区分各种
memory
似乎没有线程调度?
nvcc ..
cuda.h ..
4
• CUDA_LIB_PATH -- 链接库文件的目录,默认为 C:\CUDA\lib
在 Visual Studio 中使用 CUDA
CUDA 的主要工具是 nvcc,它会执行所需要的程序,将 CUDA 程序代码编译成执行档 (或
object 檔) 。在 Visual Studio 下,我们透过设定 custom build tool 的方式,让 Visual Studio 会
自动执行 nvcc。
这里以 Visual Studio 2005 为例:
1. 首先,建立一个 Win32 Console 模式的 project(在 Application Settings 中记得勾选
Empty project),并新增一个档案,例如 main.cu。
2. 在 main.cu 上右键单击,并选择 Properties。点选 General,确定 Tool 的部份是选
择 Custom Build Tool。
3. 选择 Custom Build Step,在 Command Line 使用以下设定:
o Release 模式: "$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)bin" -c
-DWIN32 -D_CONSOLE -D_MBCS -Xcompiler /EHsc,/W3,/nologo,/Wp64,/O2,/Zi,/MT
-I"$(CUDA_INC_PATH)" -o $(ConfigurationName)\$(InputName).obj $(InputFileName)
o Debug 模式: "$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)bin" -c
-D_DEBUG -DWIN32 -D_CONSOLE -D_MBCS -Xcompiler
/EHsc,/W3,/nologo,/Wp64,/Od,/Zi,/RTC1,/MTd -I"$(CUDA_INC_PATH)" -o
$(ConfigurationName)\$(InputName).obj $(InputFileName)
4. 如果想要使用软件仿真的模式,可以新增两个额外的设定:
o EmuRelease 模式: "$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)bin"
-deviceemu -c -DWIN32 -D_CONSOLE -D_MBCS -Xcompiler
/EHsc,/W3,/nologo,/Wp64,/O2,/Zi,/MT -I"$(CUDA_INC_PATH)" -o
$(ConfigurationName)\$(InputName).obj $(InputFileName)
o EmuDebug 模式: "$(CUDA_BIN_PATH)\nvcc.exe" -ccbin "$(VCInstallDir)bin"
-deviceemu -c -D_DEBUG -DWIN32 -D_CONSOLE -D_MBCS -Xcompiler
/EHsc,/W3,/nologo,/Wp64,/Od,/Zi,/RTC1,/MTd -I"$(CUDA_INC_PATH)" -o
$(ConfigurationName)\$(InputName).obj $(InputFileName)
5. 对所有的配置文件,在 Custom Build Step 的 Outputs 中加入
$(ConfigurationName)\$(InputName).obj。
6. 选择 project,右键单击选择 Properties,再点选 Linker。对所有的配置文件修改以
下设定:
o General/Enable Incremental Linking:No
o General/Additional Library Directories:$(CUDA_LIB_PATH)
o Input/Additional Dependencies:cudart.lib
这样应该就可以直接在 Visual Studio 的 IDE 中,编辑 CUDA 程序后,直接 build 以及执
行程序了。
第一个CUDA程序
CUDA 目前有两种不同的 API:Runtime API 和 Driver API,两种 API 各有其适用的范围。
由于 runtime API 较容易使用,一开始我们会以 runetime API 为主。
CUDA 的初始化
首先,先建立一个档案 first_cuda.cu。如果是使用 Visual Studio 的话,则请先按照这里的设
定方式设定 project。
5
要使用 runtime API 的时候,需要 include cuda_runtime.h。所以,在程序的最前面,加上
#include <stdio.h>
#include <cuda_runtime.h>
接下来是一个 InitCUDA 函式,会呼叫 runtime API 中,有关初始化 CUDA 的功能:
bool InitCUDA()
{
int count;
cudaGetDeviceCount(&count);
if(count == 0) {
fprintf(stderr, "There is no device.\n");
return false;
}
int i;
for(i = 0; i < count; i++) {
cudaDeviceProp prop;
if(cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
if(prop.major >= 1) {
break;
}
}
}
if(i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
cudaSetDevice(i);
return true;
}
这个函式会先呼叫 cudaGetDeviceCount 函式,取得支持 CUDA 的装置的数目。如果系统上
没有支持 CUDA 的装置,则它会传回 1,而 device 0 会是一个仿真的装置,但不支持 CUDA
1.0 以上的功能。所以,要确定系统上是否有支持 CUDA 的装置,需要对每个 device 呼叫
cudaGetDeviceProperties 函式,取得装置的各项数据,并判断装置支持的 CUDA 版本
(prop.major 和 prop.minor 分别代表装置支持的版本号码,例如 1.0 则 prop.major 为 1 而
prop.minor 为 0)。
透过 cudaGetDeviceProperties 函式可以取得许多数据,除了装置支持的 CUDA 版本之外,
还有装置的名称、内存的大小、最大的 thread 数目、执行单元的频率等等。详情可参考
NVIDIA 的 CUDA Programming Guide。
在找到支持 CUDA 1.0 以上的装置之后,就可以呼叫 cudaSetDevice 函式,把它设为目前要
使用的装置。
最后是 main 函式。在 main 函式中我们直接呼叫刚才的 InitCUDA 函式,并显示适当的讯
息:
使用nvcc编译时,好像会自动加cuda相关的header
剩余30页未读,继续阅读
资源评论
hhq420684
- 粉丝: 34
- 资源: 30
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功