CUDA

CUDA（Compute Unified Devices Architectured，统一计算架构）是由英伟达（NVIDIA）所推出的一种软硬体整合技术，是该公司对于GPGPU的正式名称。透过这个技术，使用者可利用NVIDIA的GPU进行图像处理之外的运算，亦是首次可以利用GPU作为C-编译器的开发环境。CUDA 开发套件（CUDA Toolkit ）只能将自家的CUDA C-语言（对OpenCL只有链接的功能），也就是执行于GPU的部分编译成中间语言或是特定NVIDIA GPU架构的机器码（NVIDIA 官方称为 "device code"）；而执行于中央处理器部分的C / C++程式码（NVIDIA 官方称为 "host code"）仍依赖于外部的编译器，如Microsoft Windows下需要Microsoft Visual Studio；Linux下则主要依赖于GCC。
简介.
GPU不仅用于进行图形渲染，而且用于物理运算（物理效果如碎片、烟、火、流体）如PhysX和Bullet。进一步的，GPU可以用在计算生物学与密码学等领域的非图形应用上。在NVIDIA收购AGEIA后，NVIDIA取得相关的物理加速技术，即是PhysX物理引擎。配合CUDA技术，显示卡可以模拟成一颗PhysX物理加速晶片。目前，全系列的GeForce 8显示核心都支援CUDA。而NVIDIA亦不会再推出任何的物理加速卡，显示卡将会取代相关产品。
而使用CUDA技术，GPU可以用来进行通用处理（不仅仅是图形）；这种方法被称为GPGPU。与CPU不同的是，GPU以较慢速度并行大量执行绪，而非快速执行单一执行绪。以GeForce 8800 GTX为例，其核心拥有128个内处理器。利用CUDA技术，就可以将那些内处理器做为线程处理器，以解决数据密集的计算。而各个内处理器能够交换、同步和共享数据。GeForce 8800 GTX的运算能力可达到520GFlops，如果建设SLI系统，就可以达到1TFlops。
目前，已有软体厂商利用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技术有下列几个优点：
显卡的受支持情况.
'*' – 仅限 OEM 产品
详情请参见Nvidia:
应用.
利用CUDA技术，配合适当的软体（例如MediaCoder、Freemake Video Converter），就可以利用显示核心进行高清视频编码加速。视频解码方面，同样可以利用CUDA技术实现。此前，NVIDIA的显示核心本身已集成PureVideo单元。可是，实现相关加速功能的一个微软API－DXVA，偶尔会有加速失效问题。所以利用CoreAVC配合CUDA，变相在显示核心上实现软体解码，解决兼容性问题。另外，配合适当的引擎，显示核心就可以计算光线跟踪。NVIDIA就放出了自家的Optix实时光线跟踪引擎，透过CUDA技术利用GPU计算光线跟踪。
支援的产品.
所有基于G80及之后架构的民用与专业显示卡或运算模组皆支援CUDA技术。
示例.
CUDA Driver API.
下面将示范以最底层的CUDA Driver API 调用GPU做列向量的加法，以下为 CPU 端的程式码<syntaxhighlight lang="c">
// 本范例修改自 Andrei de A. Formiga (2012-06-04) 写的范例: https://gist.github.com/tautologico/2879581
// 编译指令 nvcc -O3 -lcuda add.c -o add.exe
// 利用CUDA函数的错误传回做例外处理
inline void checkCudaErrors( CUresult err)
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 main(int argc, char **argv)
</syntaxhighlight>而以下是GPU端的程式码
<syntaxhighlight lang="c">
// 本范例修改自 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
extern "C" __global__ void matSum(int *a, int *b, int *c)
</syntaxhighlight>
CUDA Runtime API.
下列的范例是以相较于 Driver API 来说比较简便的 CUDA Runtime API 做列向量的加法：
<syntaxhighlight lang="c">
// 本范例修改自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 中间码。
// Device code: 送入GPU执行的部分
__global__ void VecAdd(float* A, float* B, float* C)
// 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 = (float)rand() / (float)RAND_MAX;
 h_B = (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++){
 // 释放 GPU 记忆体
 cudaFree(d_A);
 cudaFree(d_B);
 cudaFree(d_C);
 // 释放 CPU 记忆体
 free(h_B);
 free(h_C);
</syntaxhighlight>
Python.
以下以pycuda ，间接调用 CUDA 做列向量的阿达玛乘积(也就是元素间乘积而非内积)
<syntaxhighlight lang="python">
import pycuda.driver as drv #CUDA drivers
import pycuda.autoinit # 自动初始化CUDA
import numpy # 矩阵运算
mod = drv.SourceModule("""
__global__ void multiply_them(float *c, float *a, float *b)
""")
multiply_them = mod.get_function("multiply_them")
h_a = numpy.random.randn (400).astype(numpy.float32)
h_b = numpy.random.randn (400).astype(numpy.float32)
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(
print (d_c)
</syntaxhighlight>
也可以用pycublas 间接调用 CUDA ，来计算矩阵乘法
<syntaxhighlight lang="python">
import numpy
from pycublas import CUBLASMatrix
A = CUBLASMatrix(numpy.mat(,numpy.float32))
B = CUBLASMatrix(numpy.mat(,numpy.float32))
C = A*B
print(C.np_mat())
</syntaxhighlight>