CUDACompute Unified Device Architecture,统一计算架构[1])是由英伟达NVIDIA所推出的一种硬件集成技术,是该公司对于GPGPU的正式名称。透过这个技术,用户可利用NVIDIA的GPU进行图像处理之外的运算,亦是首次可以利用GPU作为C-编译器的开发环境。CUDA 开发包(CUDA Toolkit )只能将自家的CUDA C-语言(对OpenCL只有链接的功能[2]),也就是执行于GPU的部分编译成PTX英语Parallel Thread Execution中间语言或是特定NVIDIA GPU架构的机器代码NVIDIA 官方称为 "device code");而执行于中央处理器部分的C / C++代码(NVIDIA 官方称为 "host code")仍依赖于外部的编译器,如Microsoft Windows下需要Microsoft Visual StudioLinux下则主要依赖于GCC[3][4][5]

CUDA
开发者NVIDIA
首次发布2007年6月23日,​16年前​(2007-06-23
当前版本11.7.0(2022年5月11日,​23个月前​(2022-05-11
操作系统WindowsmacOSLinux
类型GPGPU
许可协议专有软件
网站developer.nvidia.com/cuda-zone

简介 编辑

 
CUDA的资料传输
1.将主存的资料传入GPU存储器
2. CPU指令驱动GPU
3. GPU平行运算
4. 将运算结果自GPU存储器传回主存

GPU不仅用于进行图形渲染,而且用于物理运算(物理效果如碎片、烟、火、流体)如PhysX和Bullet。进一步的,GPU可以用在计算生物学与密码学等领域的非图形应用上。在NVIDIA收购AGEIA后,NVIDIA获取相关的物理加速技术,即是PhysX物理引擎。配合CUDA技术,显卡可以模拟成一颗PhysX物理加速芯片[6]。目前,全系列的GeForce 8显示核心都支持CUDA。而NVIDIA亦不会再推出任何的物理加速卡,显卡将会取代相关产品。

而使用CUDA技术,GPU可以用来进行通用处理(不仅仅是图形);这种方法被称为GPGPU。与CPU不同的是,GPU以较慢速度并发大量线程,而非快速执行单一线程。以GeForce 8800 GTX为例,其核心拥有128个内处理器。利用CUDA技术,就可以将那些内处理器做为线程处理器,以解决数据密集的计算。而各个内处理器能够交换、同步和共享数据。GeForce 8800 GTX的运算能力可达到520GFlops,如果建设SLI系统,就可以达到1TFlops。[7]

目前,已有软件厂商利用CUDA技术,研发出Adobe Premiere Pro的插件。通过插件,用户就可以利用显示核心去加速H.264/MPEG-4 AVC的编码速度。速度是单纯利用CPU作软件加速的7倍左右。

虽然CUDA底层是以C/C++为主,并以使用“NVCC”——NVIDIA基于LLVM的C/C++编译器接口来进行编译,但程序师也可以使用编译器指令(如OpenACC)以及多种程序设计语言扩展对CUDA平台进行操作。如Fortran程序师可以使用“CUDA Fortran”,或PGI公司的PGI CUDA Fortran 编译器进行编译。除了此之外CUDA平台还支持其它计算接口,如Khronos Group的OpenCL,Microsoft的DirectCompute,以及C++AMP。也可以透过其他语言如 Python,Perl,Java,Ruby,Lua,Haskell,MATLAB,IDL及Mathematica 的接口间接调用CUDA。

CUDA最初的CUDA软件发展包(SDK)于2007年2月15日公布,同时支持Microsoft Windows和Linux。而后在第二版中加入对Mac OS X的支持(但于CUDA Toolkit 10.2起放弃对macOS的支持),取代2008年2月14日发布的测试版。所有G8x系列及以后的NVIDIA GPUs皆支持CUDA技术,包括GeForce,Quadro和Tesla系列。CUDA与大多数标准操作系统兼容。Nvidia声明:根据二进制兼容性,基于G8x系列开发的程序无需修改即可在未来所有的Nvidia显卡上运行。

优点 编辑

在GPUs(GPGPU)上使用图形APIs进行传统通用计算,CUDA技术有下列几个优点:[8]

  • 分散读取——代码可以从存储器的任意地址读取
  • 统一虚拟内存(Unified Memory, 从 CUDA 6.0 开始)—— 将所有 CPU 和 GPU 的内存置于统一管理的虚拟内存空间下。
  • 共享存储器(Global Memory)—— 访问快速的区域,使之在多个线程间共享,有效带宽比纹理存储器(Texture Memory)更大。
  • 与GPU之间更快的下载与回读
  • 全面支持整型与位操作,包括整型纹理查找

限制 编辑

  • CUDA不支持完整的C语言标准。它在C++编译器上运行主机代码时,会使一些在C中合法(但在C++中不合法)的代码无法编译。
  • 不支持纹理渲染(CUDA 3.2及以后版本通过在CUDA数组中引入“表面写操作”——底层的不透明数据结构——来进行处理)
  • 受系统主线的带宽和延迟的影响,主机与设备存储器之间资料复制可能会导致性能下降(通过过GPU的DMA引擎处理,异步存储器传输可在一定范围内缓解此现象)
  • 当线程总数为数千时,线程应按至少32个一组来运行才能获得最佳效果。如果每组中的32个线程使用相同的执行路径,则程序分支不会显著影响效果;在处理本质上不同的任务时,单指令流多数据流执行模型将成为一个瓶颈(如在光线追踪算法中遍历一个空间分割的数据结构)
  • 只有NVIDIA的GPUs支持CUDA技术
  • 由于编译器需要使用优化技术来利用有限的资源,即使合法的C/C++有时候也会被标记并中止编译
  • CUDA(计算能力1.x)使用一个不包含递归、函数指针的C语言子集,外加一些简单的扩展。而单个进程必须运行在多个不相交的存储器空间上,这与其它C语言运行环境不同。
  • CUDA(计算能力2.x)允许C++类功能的子集,如成员函数可以不是虚拟的(这个限制将在以后的某个版本中移除)[参见《CUDA C程序设计指南3.1》-附录D.6]
  • 双精度浮点(CUDA计算能力1.3及以上)与IEEE754标准有所差异:倒数、除法、平方根仅支持舍入到最近的偶数。单精确度中不支持反常值(denormal)及sNaN(signaling NaN);只支持两种IEEE舍入模式(舍位与舍入到最近的偶数),这些在每条指令的基础上指定,而非控制字码;除法/平方根的精度比单精确度略低。

显卡的受支持情况 编辑

不同 CUDA 版本支持的计算能力列表[9]
CUDA 版本 支持的计算能力 微架构 备注
1.0[10] 1.0 – 1.1 Tesla英语Tesla (microarchitecture)
1.1 1.0 – 1.1+x Tesla英语Tesla (microarchitecture)
2.0 1.0 – 1.1+x Tesla英语Tesla (microarchitecture)
2.1 – 2.3.1[11][12][13][14] 1.0 – 1.3 Tesla英语Tesla (microarchitecture)
3.0 – 3.1[15][16] 1.0 – 2.0 Tesla英语Tesla (microarchitecture), Fermi
3.2[17] 1.0 – 2.1 Tesla英语Tesla (microarchitecture), Fermi
4.0 – 4.2 1.0 – 2.1+x Tesla英语Tesla (microarchitecture), Fermi
5.0 – 5.5 1.0 – 3.5 Tesla英语Tesla (microarchitecture), Fermi, Kepler
6.0 1.0 – 3.5 Tesla英语Tesla (microarchitecture), Fermi, Kepler
6.5 1.1 – 5.x Tesla英语Tesla (microarchitecture), Fermi, Kepler, Maxwell英语Maxwell (microarchitecture) 最后支持计算能力 1.x (Tesla英语Tesla (microarchitecture)) 的版本
7.0 – 7.5 2.0 – 5.x Fermi, Kepler, Maxwell英语Maxwell (microarchitecture)
8.0 2.0 – 6.x Fermi, Kepler, Maxwell英语Maxwell (microarchitecture), Pascal 最后支持计算能力 2.x (Fermi) 的版本;GTX 1070Ti 不受支持
9.0 – 9.2 3.0 – 7.2 Kepler, Maxwell英语Maxwell (microarchitecture), Pascal, Volta Pascal GTX 1070Ti 不受 CUDA SDK 9.0 支持,但受 CUDA SDK 9.2支持
10.0 – 10.2 3.0 – 7.5 Kepler, Maxwell英语Maxwell (microarchitecture), Pascal, Volta, Turing 最后支持计算能力 3.x (Kepler) 的版本;CUDA SDK 10.2 是最后能用于 macOS 的官方版本,在未来的版本中 macOS 将不被支持
11.0 – 3.5 - 8.6 Maxwell英语Maxwell (microarchitecture), Pascal, Volta, Turing, Ampere
不同计算能力对应的显卡列表
计算能力(版本) 微架构 GPU GeForce 系列 Quadro NVS 系列 Tesla 系列 Tegra 系列,
Jetson 系列,
DRIVE 系列
1.0 Tesla英语Tesla (microarchitecture) G80 GeForce 8800 Ultra, GeForce 8800 GTX, GeForce 8800 GTS(G80) Quadro FX 5600, Quadro FX 4600, Quadro Plex 2100 S4 Tesla C870, Tesla D870, Tesla S870
1.1 G92, G94, G96, G98, G84, G86 GeForce GTS 250, GeForce 9800 GX2, GeForce 9800 GTX, GeForce 9800 GT, GeForce 8800 GTS(G92), GeForce 8800 GT, GeForce 9600 GT, GeForce 9500 GT, GeForce 9400 GT, GeForce 8600 GTS, GeForce 8600 GT, GeForce 8500 GT,
GeForce G110M, GeForce 9300M GS, GeForce 9200M GS, GeForce 9100M G, GeForce 8400M GT, GeForce G105M
Quadro FX 4700 X2, Quadro FX 3700, Quadro FX 1800, Quadro FX 1700, Quadro FX 580, Quadro FX 570, Quadro FX 470, Quadro FX 380, Quadro FX 370, Quadro FX 370 Low Profile, Quadro NVS 450, Quadro NVS 420, Quadro NVS 290, Quadro NVS 295, Quadro Plex 2100 D4,
Quadro FX 3800M, Quadro FX 3700M, Quadro FX 3600M, Quadro FX 2800M, Quadro FX 2700M, Quadro FX 1700M, Quadro FX 1600M, Quadro FX 770M, Quadro FX 570M, Quadro FX 370M, Quadro FX 360M, Quadro NVS 320M, Quadro NVS 160M, Quadro NVS 150M, Quadro NVS 140M, Quadro NVS 135M, Quadro NVS 130M, Quadro NVS 450, Quadro NVS 420,[18] Quadro NVS 295
1.2 GT218, GT216, GT215 GeForce GT 340*, GeForce GT 330*, GeForce GT 320*, GeForce 315*, GeForce 310*, GeForce GT 240, GeForce GT 220, GeForce 210,
GeForce GTS 360M, GeForce GTS 350M, GeForce GT 335M, GeForce GT 330M, GeForce GT 325M, GeForce GT 240M, GeForce G210M, GeForce 310M, GeForce 305M
Quadro FX 380 Low Profile, Quadro FX 1800M, Quadro FX 880M, Quadro FX 380M,
Nvidia NVS 300, NVS 5100M, NVS 3100M, NVS 2100M, ION
1.3 GT200, GT200b GeForce GTX 295, GTX 285, GTX 280, GeForce GTX 275, GeForce GTX 260 Quadro FX 5800, Quadro FX 4800, Quadro FX 4800 for Mac, Quadro FX 3800, Quadro CX, Quadro Plex 2200 D2 Tesla C1060, Tesla S1070, Tesla M1060
2.0 Fermi英语Fermi (microarchitecture) GF100, GF110 GeForce GTX 590, GeForce GTX 580, GeForce GTX 570, GeForce GTX 480, GeForce GTX 470, GeForce GTX 465,
GeForce GTX 480M
Quadro 6000, Quadro 5000, Quadro 4000, Quadro 4000 for Mac, Quadro Plex 7000,
Quadro 5010M, Quadro 5000M
Tesla C2075, Tesla C2050/C2070, Tesla M2050/M2070/M2075/M2090
2.1 GF104, GF106 GF108, GF114, GF116, GF117, GF119 GeForce GTX 560 Ti, GeForce GTX 550 Ti, GeForce GTX 460, GeForce GTS 450, GeForce GTS 450*, GeForce GT 640 (GDDR3), GeForce GT 630, GeForce GT 620, GeForce GT 610, GeForce GT 520, GeForce GT 440, GeForce GT 440*, GeForce GT 430, GeForce GT 430*, GeForce GT 420*,
GeForce GTX 675M, GeForce GTX 670M, GeForce GT 635M, GeForce GT 630M, GeForce GT 625M, GeForce GT 720M, GeForce GT 620M, GeForce 710M, GeForce 610M, GeForce 820M, GeForce GTX 580M, GeForce GTX 570M, GeForce GTX 560M, GeForce GT 555M, GeForce GT 550M, GeForce GT 540M, GeForce GT 525M, GeForce GT 520MX, GeForce GT 520M, GeForce GTX 485M, GeForce GTX 470M, GeForce GTX 460M, GeForce GT 445M, GeForce GT 435M, GeForce GT 420M, GeForce GT 415M, GeForce 710M, GeForce 410M
Quadro 2000, Quadro 2000D, Quadro 600,
Quadro 4000M, Quadro 3000M, Quadro 2000M, Quadro 1000M,
NVS 310, NVS 315, NVS 5400M, NVS 5200M, NVS 4200M
3.0 Kepler GK104, GK106, GK107 GeForce GTX 770, GeForce GTX 760, GeForce GT 740, GeForce GTX 690, GeForce GTX 680, GeForce GTX 670, GeForce GTX 660 Ti, GeForce GTX 660, GeForce GTX 650 Ti BOOST, GeForce GTX 650 Ti, GeForce GTX 650,
GeForce GTX 880M, GeForce GTX 780M, GeForce GTX 770M, GeForce GTX 765M, GeForce GTX 760M, GeForce GTX 680MX, GeForce GTX 680M, GeForce GTX 675MX, GeForce GTX 670MX, GeForce GTX 660M, GeForce GT 750M, GeForce GT 650M, GeForce GT 745M, GeForce GT 645M, GeForce GT 740M, GeForce GT 730M, GeForce GT 640M, GeForce GT 640M LE, GeForce GT 735M, GeForce GT 730M
Quadro K5000, Quadro K4200, Quadro K4000, Quadro K2000, Quadro K2000D, Quadro K600, Quadro K420,
Quadro K500M, Quadro K510M, Quadro K610M, Quadro K1000M, Quadro K2000M, Quadro K1100M, Quadro K2100M, Quadro K3000M, Quadro K3100M, Quadro K4000M, Quadro K5000M, Quadro K4100M, Quadro K5100M,
NVS 510, Quadro 410
Tesla K10, GRID K340, GRID K520
3.2 GK20A Tegra K1,
Jetson TK1
3.5 GK110, GK208 GeForce GTX Titan Z, GeForce GTX Titan Black, GeForce GTX Titan, GeForce GTX 780 Ti, GeForce GTX 780, GeForce GT 640 (GDDR5), GeForce GT 630 v2, GeForce GT 730, GeForce GT 720, GeForce GT 710, GeForce GT 740M (64-bit, DDR3), GeForce GT 920M Quadro K6000, Quadro K5200 Tesla K40, Tesla K20x, Tesla K20
3.7 GK210 Tesla K80
5.0 Maxwell英语Maxwell (microarchitecture) GM107, GM108 GeForce GTX 750 Ti, GeForce GTX 750, GeForce GTX 960M, GeForce GTX 950M, GeForce 940M, GeForce 930M, GeForce GTX 860M, GeForce GTX 850M, GeForce 845M, GeForce 840M, GeForce 830M, GeForce GTX 870M Quadro K1200, Quadro K2200, Quadro K620, Quadro M2000M, Quadro M1000M, Quadro M600M, Quadro K620M, NVS 810 Tesla M10
5.2 GM200, GM204, GM206 GeForce GTX Titan X, GeForce GTX 980 Ti, GeForce GTX 980, GeForce GTX 970, GeForce GTX 960, GeForce GTX 950, GeForce GTX 750 SE,
GeForce GTX 980M, GeForce GTX 970M, GeForce GTX 965M
Quadro M6000 24GB, Quadro M6000, Quadro M5000, Quadro M4000, Quadro M2000, Quadro M5500,
Quadro M5000M, Quadro M4000M, Quadro M3000M
Tesla M4, Tesla M40, Tesla M6, Tesla M60
5.3 GM20B Tegra X1,
Jetson TX1,
Jetson Nano,
DRIVE CX,
DRIVE PX
6.0 Pascal GP100 Quadro GP100 Tesla P100
6.1 GP102, GP104, GP106, GP107, GP108 Nvidia TITAN Xp, Titan X,
GeForce GTX 1080 Ti, GTX 1080, GTX 1070 Ti, GTX 1070, GTX 1060, GTX 1050 Ti, GTX 1050,
GT 1030, MX350, MX330, MX250, MX230, MX150
Quadro P6000, Quadro P5000, Quadro P4000, Quadro P2200, Quadro P2000, Quadro P1000, Quadro P400, Quadro P500, Quadro P520, Quadro P600,
Quadro P5000(Mobile), Quadro P4000(Mobile), Quadro P3000(Mobile)
Tesla P40, Tesla P6, Tesla P4
6.2 GP10B[19] Tegra X2, Jetson TX2, DRIVE PX 2
7.0 Volta GV100 NVIDIA TITAN V Quadro GV100 Tesla V100, Tesla V100S
7.2 GV10B[20] Tegra Xavier,
Jetson Xavier NX,
Jetson AGX Xavier, DRIVE AGX Xavier, DRIVE AGX Pegasus
7.5 Turing TU102, TU104, TU106, TU116, TU117 NVIDIA TITAN RTX,
GeForce RTX 2080 Ti, RTX 2080 Super, RTX 2080, RTX 2070 Super, RTX 2070, RTX 2060 Super, RTX 2060,
GeForce GTX 1660 Ti, GTX 1660 Super, GTX 1660, GTX 1650 Super, GTX 1650

GeForce MX450

Quadro RTX 8000, Quadro RTX 6000, Quadro RTX 5000, Quadro RTX 4000,
Quadro T2000, Quadro T1000
Tesla T4
8.0 Ampere GA100, GA102, GA104, GA106 NVIDIA Geforce RTX 3090,
Geforce RTX 3080, RTX 3070 , RTX 3060Ti, RTX 3060
A100

'*' – 仅限 OEM 产品

详情请参见Nvidia页面存档备份,存于互联网档案馆):

应用 编辑

利用CUDA技术,配合适当的软件(例如MediaCoder[21]、Freemake Video Converter),就可以利用显示核心进行高清视频编码加速。视频解码方面,同样可以利用CUDA技术实现。此前,NVIDIA的显示核心本身已集成PureVideo单元。可是,实现相关加速功能的一个微软API-DXVA,偶尔会有加速失效问题。所以利用CoreAVC配合CUDA,变相在显示核心上实现软件解码,解决兼容性问题[22]。另外,配合适当的引擎,显示核心就可以计算光线跟踪。NVIDIA就放出了自家的Optix实时光线跟踪引擎,透过CUDA技术利用GPU计算光线跟踪[23]

支持的产品 编辑

所有基于G80及之后架构的民用与专业显卡或运算模块皆支持CUDA技术[24]

示例 编辑

CUDA Driver API 编辑

下面将示范以最底层的CUDA Driver API页面存档备份,存于互联网档案馆)调用GPU做列向量的加法,以下为 CPU 端的代码

// 本範例修改自 Andrei de A. Formiga (2012-06-04) 寫的範例: https://gist.github.com/tautologico/2879581
// 編譯指令 nvcc -O3 -lcuda add.c -o add.exe

#include <stdio.h>
#include <stdlib.h>

#include <cuda.h>
#include <builtin_types.h> // Driver api 的型態定義

#define N 1024 //列向量長度

// 利用CUDA函數的錯誤傳回做例外處理
inline void checkCudaErrors( CUresult err)
{
    if( CUDA_SUCCESS != err) {
        printf("CUDA Driver API error = %04d from file <%s>, line %i.\n",
                err, __FILE__, __LINE__ );
        exit(-1); // 直接終止程式
    }
}

CUdevice   device; // CUDA 裝置(也就是GPU)物件
CUcontext  context; // CUDA 內容物件
CUmodule   module; // 代表GPU程式碼的物件
CUfunction function; // CUDA GPU 函數
size_t     totalGlobalMem; // CUDA 裝置記憶體總量

// Driver API 只能自外部檔案讀取 GPU 程式,可以為 PTX 中間碼也可以是 cubin 機器碼(或是混合各種架構機器碼的fatbin)
char       *module_file = (char*) "matSumKernel.cubin";

// GPU 函數名稱
char       *kernel_name = (char*) "matSum";

// 初始化 CUDA 的手續
void initCUDA()
{
    int deviceCount = 0; // 當前可使用的 CUDA 裝置(GPU)數
    CUresult err = cuInit(0); // 初始化 CUDA API

    if (err == CUDA_SUCCESS) // 取得可用裝置數
        checkCudaErrors(cuDeviceGetCount(&deviceCount));

    if (deviceCount == 0) { // 確定有可用的裝置
        fprintf(stderr, "Error: no devices supporting CUDA\n");
        exit(-1);
    }

    // get first CUDA device
    checkCudaErrors(cuDeviceGet(&device, 0)); // 取編號為 0 的裝置
    char name[100];
    cuDeviceGetName(name, 100, device); // 印出裝置名稱
    printf("> Using device 0: %s\n", name);

    checkCudaErrors( cuDeviceTotalMem(&totalGlobalMem, device) ); 
    // 印出裝置可用記憶體
    printf("  Total amount of global memory:   %llu bytes\n",
           (unsigned long long)totalGlobalMem);
    // GPU 記憶體是否是為64bits定址
    printf("  64-bit Memory Address:           %s\n",
           (totalGlobalMem > (unsigned long long)4*1024*1024*1024L)?
           "YES" : "NO");

    // 創建 CUDA 內容
    err = cuCtxCreate(&context, 0, device);
    if (err != CUDA_SUCCESS) {
        fprintf(stderr, "* Error initializing the CUDA context.\n");
        cuCtxDetach(context);
        exit(-1);
    }

    // 讀取編譯好的cubin GPU程式碼
    err = cuModuleLoad(&module, module_file);
    if (err != CUDA_SUCCESS) {
        fprintf(stderr, "* Error loading the module %s\n", module_file);
        cuCtxDetach(context); // 釋放 CUDA 內容物件
        exit(-1);
    }

    // 獲取GPU程式裡函數"matSum"的指標
    err = cuModuleGetFunction(&function, module, kernel_name);

    if (err != CUDA_SUCCESS) {
        fprintf(stderr, "* Error getting kernel function %s\n", kernel_name);
        cuCtxDetach(context);
        exit(-1);
    }
}

int main(int argc, char **argv)
{
    int a[N], b[N], c[N];
    CUdeviceptr d_a, d_b, d_c;

    // 注意 GPU 變數指標的型態是 CUdeviceptr
    // typedef unsigned int CUdeviceptr_v2
    // typedef CUdeviceptr_v2 CUdeviceptr

    // 初始化主記憶體變數
    for (int i = 0; i < N; ++i) {
        a[i] = i;
        b[i] = N - i;
    }

    initCUDA();

    // 動態分配 GPU 記憶體
    // CUresult cuMemAlloc ( CUdeviceptr* dptr, size_t bytesize )
    checkCudaErrors( cuMemAlloc(&d_a, sizeof(int) * N) ); // 
    checkCudaErrors( cuMemAlloc(&d_b, sizeof(int) * N) );
    checkCudaErrors( cuMemAlloc(&d_c, sizeof(int) * N) );

    // 將列向量傳入裝置
    // CUresult cuMemcpyHtoD ( CUdeviceptr dstDevice, const void* srcHost, size_t ByteCount ) 
    checkCudaErrors( cuMemcpyHtoD(d_a, a, sizeof(int) * N) );
    checkCudaErrors( cuMemcpyHtoD(d_b, b, sizeof(int) * N) );

    void *args[3] = { &d_a, &d_b, &d_c }; // 包裝放入GPU 函數的引數
    
    // 運行 GPU 函數
    // CUresult cuLaunchKernel ( CUfunction f, unsigned int  gridDimX, unsigned int  gridDimY, unsigned int  gridDimZ,
    //                           unsigned int  blockDimX, unsigned int  blockDimY, unsigned int  blockDimZ,
    //                           unsigned int  sharedMemBytes, CUstream hStream, void** kernelParams, void** extra )
    
    checkCudaErrors( cuLaunchKernel(function, N, 1, 1,  // Nx1x1 blocks
                                    1, 1, 1,            // 1x1x1 threads
                                    0, 0, args, 0) );

    // 將運算結果送回主記憶體
    // CUresult cuMemcpyDtoH ( void* dstHost, CUdeviceptr srcDevice, size_t ByteCount ) 
    checkCudaErrors( cuMemcpyDtoH(c, d_c, sizeof(int) * N) );
    
    // 將 CPU 和 GPU 運算結果做對照 
    for (int i = 0; i < N; ++i) {
        if (c[i] != a[i] + b[i])
            printf("* Error at array position %d: Expected %d, Got %d\n",
                   i, a[i]+b[i], c[i]);
    }

    // 釋放 GPU 記憶體
    // CUresult cuMemFree ( CUdeviceptr dptr ) 
    checkCudaErrors( cuMemFree(d_a) );
    checkCudaErrors( cuMemFree(d_b) );
    checkCudaErrors( cuMemFree(d_c) );

    cuCtxDetach(context); // 釋放 CUDA 內容物件
    return 0;
}

而以下是GPU端的代码

// 本範例修改自 Andrei de A. Formiga (2012-06-04) 寫的範例: https://gist.github.com/tautologico/2879581
// 此部分要先編譯成 cubin 後才可以被 CPU 端程式使用
// 編譯指令 nvcc -O3 -cubin -arch=native matSumKernel.cu -o matSumKernel.cubin

#define N 1024 //列向量長度

extern "C" __global__ void matSum(int *a, int *b, int *c)
{
    int tid = blockIdx.x; // thread 的 x 座標
    if (tid < N)
        c[tid] = a[tid] + b[tid]; //每個 thread 做一次加法
}

CUDA Runtime API 编辑

下列的示例是以相较于 Driver API 来说比较简便的 CUDA Runtime API页面存档备份,存于互联网档案馆) 做列向量的加法:

// 本範例修改自Nvidia官方的CUDA開發指引: https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#kernels
// 編譯指令 nvcc vector_add.cu -arch=native -o vector_add.exe
// -arch=native 代表將 device code 編譯成當前電腦 Nvidia GPU 架構的機器碼,拿掉就是照預設編譯成 PTX 中間碼。

#include <stdio.h>
#include <stdlib.h> // 引用動態分配 malloc、隨機函數 rand() 和隨機上限 RAND_MAX

#define N 1024 // 列向量長度

// Device code: 送入GPU執行的部分

__global__ void VecAdd(float* A, float* B, float* C)
{
    int i = threadIdx.x; // thread 的 x 座標
    if (i < N){
        C[i] = A[i] + B[i]; // 每個 thread 作一次加法
    }
}
            
// Host code: 送入CPU執行的部分

int main()
{
	size_t size = N * sizeof(float); // 向量的實際大小,以位元組(bytes)為單位
	
	int i; // 迴圈計數

	// 動態分配位於"host(CPU) 記憶體" 的向量
	float* h_A = (float*)malloc(size);
	float* h_B = (float*)malloc(size);
	float* h_C = (float*)malloc(size);

	// 隨機初始化輸入向量
	for(i = 0; i < N; i++){
		h_A[i] = (float)rand() / (float)RAND_MAX;
		h_B[i] = (float)rand() / (float)RAND_MAX;
	}

	// 動態分配位於"device(GPU) 記憶體"的向量
	float* d_A;
	cudaMalloc(&d_A, size); // cudaError_t cudaMalloc ( void** devPtr, size_t size )
	float* d_B;
	cudaMalloc(&d_B, size);
	float* d_C;
	cudaMalloc(&d_C, size);

	// 將向量從 CPU 複製到 GPU
	cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);
	cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);
	
	// 將 device code 送入 GPU 並執行,執行時一個 Grid 只有一個 block ,一個 block 有 N 個 thread
	VecAdd<<<1, N>>>(d_A, d_B, d_C);

	// 將算好的向量從 GPU 複製到 CPU
	cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);
	
	// 印出運算結果
	for(i = 0; i < N; i++){
        printf("%f ", h_C[i]);
	}

	// 釋放 GPU 記憶體
	cudaFree(d_A);
	cudaFree(d_B);
	cudaFree(d_C);
 
	// 釋放 CPU 記憶體
    free(h_A);
	free(h_B);
	free(h_C);
}

Python 编辑

以下以pycuda页面存档备份,存于互联网档案馆),间接调用 CUDA 做列向量阿达玛乘积(也就是元素间乘积而非内积)

import pycuda.driver as drv #CUDA drivers
import pycuda.autoinit # 自動初始化CUDA

import numpy # 矩陣運算

# 讀取並編譯GPU執行的程式碼,以 CUDA-C 寫成

mod = drv.SourceModule("""
__global__ void multiply_them(float *c, float *a, float *b)
{
  int i = threadIdx.x;
  c[i] = a[i] * b[i];
}
""")

# 獲取GPU程式碼中的 multiply_them 函數
multiply_them = mod.get_function("multiply_them")

#生成兩個常態隨機分布的浮點數ndarray,shape 為(400, )
h_a = numpy.random.randn (400).astype(numpy.float32)
h_b = numpy.random.randn (400).astype(numpy.float32)

# 儲存結果的 h_c 列向量,其 shape 和向量 a 相同但值為零
h_c = numpy.zeros_like (a)

'''
執行GPU的函數:
注意這裡h_a, h_b 和 h_c 是在CPU記憶體的 python 變數
以下按照原來 GPU 程式碼的區域變數順序,指定哪些是從CPU傳入GPU (drv.In);哪些是從GPU傳入CPU (drv.Out),
簡單來說,自 GPU 傳出到 h_c; h_a 傳入 GPU;h_b 傳入 GPU。
(400,1,1) 代表一個block裡有的thread數量為 400 x 1 x 1
'''
multiply_them(
        drv.Out(h_c), drv.In(h_a), drv.In(h_b),
        block=(400,1,1))

#印出結果
print (d_c)

也可以用pycublas 间接调用 CUDA ,来计算矩阵乘法

import numpy
from pycublas import CUBLASMatrix
# 以 numpy 定義矩陣並傳入 CUBLASMatrix
A = CUBLASMatrix(numpy.mat([[1,2,3],[4,5,6]],numpy.float32))
B = CUBLASMatrix(numpy.mat([[2,3],[4,5],[6,7]],numpy.float32))
# 以 CUBLASMatrix 做矩陣乘法
C = A*B
# 將運算結果轉回 numpy 並印出
print(C.np_mat())

相关条目 编辑

参考文献 编辑

  1. ^ CUDA是Compute Unified Device Architecture(统一计算架构)的简称. [2010-02-08]. (原始内容存档于2010-02-13). 
  2. ^ Nvcc compiler and OpenCL kernel. NVIDIA Developer Forums. 2021-05-25 [2022-06-22]. (原始内容存档于2022-06-27) (中文(中国大陆)). 
  3. ^ NVIDIA CUDA Compiler Driver NVCC. docs.nvidia.com. [2022-06-22]. (原始内容存档于2022-08-11) (美国英语). 
  4. ^ CUDA Installation Guide for Microsoft Windows. docs.nvidia.com. [2022-06-23]. (原始内容存档于2022-08-10) (美国英语). 
  5. ^ NVIDIA CUDA Installation Guide for Linux. docs.nvidia.com. [2022-06-23]. (原始内容存档于2022-09-04) (美国英语). 
  6. ^ 全系列GeForce 8显卡将获得PhysX物理支持. [2008-05-02]. (原始内容存档于2008-04-17). 
  7. ^ 小熊在线-宁道奇. 双剑合璧:CPU+GPU异构计算完全解析. 小熊在线. [2013-06-18]. (原始内容存档于2013-06-24). 
  8. ^ CUDA C++ Programming Guide. docs.nvidia.com. [2022-06-23]. (原始内容存档于2021-05-03) (美国英语). 
  9. ^ CUDA Toolkit Archive. NVIDIA Developer. [2020-07-16]. (原始内容存档于2020-07-09). 
  10. ^ NVIDIA CUDA Programming Guide. Version 1.0 (PDF). 2007-06-23 [2020-07-16]. (原始内容存档 (PDF)于2018-04-17). 
  11. ^ NVIDIA CUDA Programming Guide. Version 2.1 (PDF). 2008-12-08 [2020-07-16]. (原始内容存档 (PDF)于2020-04-06). 
  12. ^ NVIDIA CUDA Programming Guide. Version 2.2 (PDF). 2009-04-02 [2020-07-16]. (原始内容存档 (PDF)于2019-03-06). 
  13. ^ NVIDIA CUDA Programming Guide. Version 2.2.1 (PDF). 2009-05-26 [2020-07-16]. (原始内容存档 (PDF)于2019-09-25). 
  14. ^ NVIDIA CUDA Programming Guide. Version 2.3.1 (PDF). 2009-08-26 [2020-07-16]. (原始内容存档 (PDF)于2012-02-18). 
  15. ^ NVIDIA CUDA Programming Guide. Version 3.0 (PDF). 2010-02-20 [2020-07-16]. (原始内容存档 (PDF)于2020-07-29). 
  16. ^ NVIDIA CUDA C Programming Guide. Version 3.1.1 (PDF). 2010-07-21 [2020-07-16]. (原始内容存档 (PDF)于2020-06-01). 
  17. ^ NVIDIA CUDA C Programming Guide. Version 3.2 (PDF). 2010-11-09 [2020-07-16]. (原始内容存档 (PDF)于2020-03-02). 
  18. ^ NVIDIA Quadro NVS 420 Specs. TechPowerUp GPU Database. [2020-07-16]. (原始内容存档于2020-07-29). 
  19. ^ Larabel, Michael. NVIDIA Rolls Out Tegra X2 GPU Support In Nouveau. Phoronix. 2017-03-29 [2017-08-08]. (原始内容存档于2017-08-09). 
  20. ^ Nvidia Xavier Specs页面存档备份,存于互联网档案馆) on TechPowerUp (preliminary)
  21. ^ CUDA转码软件. [2009-09-14]. (原始内容存档于2009-09-18). 
  22. ^ 另类CUDA高清方案. [2009-09-14]. (原始内容存档于2009-11-29). 
  23. ^ NVIDIA Optix实时光线追踪DEMO. [2009-09-14]. (原始内容存档于2009-09-12). 
  24. ^ NVIDIA官方支援CUDA技術的產品列表. [2010-04-19]. (原始内容存档于2010-02-18). 

外部链接 编辑