CUDA C++程序设计模型
CUDA C++程序设计模型
本章介绍了CUDA编程模型背后的主要概念,概述了它们在C++中的暴露方式。在编程接口中给出了CUDA C++的广泛描述。
使用的矢量加法示例的完整代码可以在矢量加法CUDA示例中找到。
一. 内核
CUDA C++通过允许程序员定义C++函数,称为内核,扩展了C++,当调用时,用n个不同的CUDA线程并行执行n次,而不是像常规C++函数那样只执行一次。
一个内核使用了__global__声明说明符来定义,并且使用一个新的<<<…>执行配置语法(参见C++语言扩展)来指定执行给定内核调用的内核的CUDA线程的数量。执行内核的每个线程都有一个唯一的线程ID,可以通过内置变量在内核中访问。
下面的示例代码使用内置变量threadIdx,添加两个大小为N的向量A和B,并将结果存储到向量C中:
// Kernel definition __global__ void VecAdd(float* A, float* B, float* C)
{
int i = threadIdx.x;
C[i] = A[i] + B[i];
}
int main()
{
...
//
Kernel invocation with N threads
VecAdd<<<1, N>>>(A, B,
C);
...
}
二.线程层次结构
为了方便起见,threadIdx是一个三分量向量,因此可以使用一维、二维或三维螺纹索引来识别螺纹,形成一维、二维或三维螺纹块,称为螺纹块。这提供了一种跨域元素(如向量、矩阵或卷积)调用计算的自然方法。
一个线程的索引和它的线程ID以一种简单的方式相互关联:对于一维模块,它们是相同的;对于二维模块(Dx,Dy),索引(x,y)线程的线程ID是(x+ydx);对于三维模块(Dx,Dy,Dz),索引(x,y,z)线程的线程ID是(x+ydx+z Dx Dy)。
例如,下面的代码添加两个大小为NxN的矩阵A和B,并将结果存储到矩阵C中:
// Kernel definition __global__ void MatAdd(float
A[N][N], float
B[N][N], float
C[N][N])
{
int i = threadIdx.x;
int j = threadIdx.y;
C[i][j] = A[i][j] + B[i][j];
}
int main()
{
...
// Kernel invocation
with one block of N * N * 1 threads
int
numBlocks = 1;
dim3
threadsPerBlock(N, N);
MatAdd<<<numBlocks,
threadsPerBlock>>>(A, B,
C);
...
}
每个块的线程数是有限制的,因为块的所有线程都应该位于同一个处理器核心上,并且必须共享该核心的有限内存资源。在当前GPU上,一个线程块最多可以包含1024个线程。但是,内核可以由多个形状相同的线程块执行,因此线程总数等于每个块的线程数乘以块的数量。
块被组织成一维、二维或三维的螺纹块网格,如图1所示。网格中线程块的数量通常由正在处理的数据的大小决定,该大小通常超过系统中处理器的数量。
图1. 螺纹块网格
语法中指定的每个块的线程数和每个网格的块数可以是int或dim3类型。二维块或网格可以在上面的示例中指定。
网格中的每个块都可以由一个一维、二维或三维唯一索引标识,该索引可通过内置的blockIdx变量在内核中访问。线程块的维度可以通过内置的blockDim变量在内核中访问。扩展前面的MatAdd()示例以处理多个块,代码如下所示。
// Kernel definition __global__
void MatAdd(float
A[N][N], float
B[N][N], float
C[N][N])
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
if (i
< N && j < N) C[i][j] = A[i][j] + B[i][j];
}
int main()
{
...
// Kernel invocation
dim3
threadsPerBlock(16, 16);
dim3
numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);
MatAdd<<<numBlocks,
threadsPerBlock>>>(A, B,
C);
...
}
16x16(256个线程)的线程块大小虽然在本例中是任意的,但却是常见的选择。网格是用足够的块创建的,每个矩阵元素有一个线程。为了简单起见,本例假设每个维度中每个网格的线程数可以被该维度中每个块的线程数均匀地整除,尽管事实并非如此。线程块需要独立执行:必须能够以任何顺序并行或串联执行它们。
这种独立性要求允许线程块在任意数量的内核上按任意顺序进行调度,使程序员能够编写随内核数量而扩展的代码。
块中的线程可以通过共享一些共享内存来协作,并通过同步它们的执行来协调内存访问。更准确地说,可以通过调用syncthreads()内部函数来指定内核中的同步点;syncthreads()充当一个屏障,块中的所有线程都必须在该屏障上等待,然后才能允许任何线程继续。共享内存给出了一个使用共享内存的示例。除了syncthreads()之外,协作组API还提供了一组丰富的线程同步原语。
为了实现高效的协作,共享内存应该是靠近每个处理器核心的低延迟内存(很像一级缓存),而syncthreads()应该是轻量级的。
三.内存层次结构
CUDA线程可以在执行期间从多个内存空间访问数据,如图2所示。每个线程都有专用的本地内存。每个线程块都有对该块的所有线程可见的共享内存,并且与该块具有相同的生存期。所有线程都可以访问相同的全局内存。
所有线程还可以访问另外两个只读内存空间:常量和纹理内存空间。全局、常量和纹理内存空间针对不同的内存使用进行了优化(请参阅设备内存访问)。对于某些特定的数据格式(请参见纹理和曲面内存),纹理内存还提供不同的寻址模式以及数据过滤。
全局、常量和纹理内存空间在同一个应用程序启动的内核之间是持久的。
图2. 内存层次结构
四.异构程序设计
如图3所示,CUDA编程模型假定CUDA线程在物理上独立的设备上执行,该设备作为运行C++程序的主机的协处理器。例如,当内核在GPU上执行,而C++程序的其余部分在CPU上执行时,情况就是这样。
CUDA编程模型还假设主机和设备都在DRAM中保持各自独立的内存空间,分别称为主机内存和设备内存。因此,程序通过调用CUDA运行时(在编程接口中描述)来管理内核可见的全局、常量和纹理内存空间。这包括设备内存分配和释放,以及主机和设备内存之间的数据传输。
统一内存提供托管内存以桥接主机和设备内存空间。托管内存可以从系统中的所有CPU和GPU访问,作为具有公共地址空间的单个相干内存映像。此功能支持设备内存的超额订阅,并且通过消除在主机和设备上显式镜像数据的需要,可以大大简化移植应用程序的任务。有关统一内存的介绍,请参见统一内存编程。
图3. 异构程序设计
注:串行代码在主机上执行,并行代码在设备上执行。
五.计算能力
设备的计算能力由版本号表示,有时也称为“SM版本”。此版本号标识GPU硬件支持的功能,并由应用程序在运行时用于确定当前GPU上可用的硬件功能和/或指令。计算能力包括主要修订号X和次要修订号Y,并用X.Y表示。
具有相同主要修订号的设备具有相同的核心体系结构。主要版本号为7(基于Volta架构的设备)、6(基于Pascal架构的设备)、5(基于Maxwell架构的设备)、3(基于Kepler架构的设备)、2(基于Fermi架构的设备)和1(基于Tesla架构的设备)。
次要修订号对应于核心架构的增量改进,可能包括新功能。
Turing是计算能力为7.5的设备的架构,是基于Volta架构的增量更新。
启用CUDA的GPU列出了所有启用CUDA的设备及其计算能力。计算能力给出了每个计算能力的技术规范。
注:特定GPU的计算能力版本不应与CUDA版本(如CUDA 7.5、CUDA 8、CUDA 9)混淆,后者是CUDA软件平台的版本。CUDA平台被应用程序开发人员用来创建运行在许多代GPU架构上的应用程序,包括尚未发明的未来GPU架构。虽然CUDA平台的新版本通常通过支持新GPU体系结构的计算能力版本来增加对新GPU体系结构的本机支持,但CUDA平台的新版本通常还包括独立于硬件生成的软件功能。
从CUDA 7.0和CUDA 9.0开始,Tesla和Fermi架构不再受支持。
CUDA C++程序设计模型的更多相关文章
- 【CUDA并行程序设计系列(1)】GPU技术简介
http://www.cnblogs.com/5long/p/cuda-parallel-programming-1.html 本系列目录: [CUDA并行程序设计系列(1)]GPU技术简介 [CUD ...
- 《CUDA并行程序设计:GPU编程指南》
<CUDA并行程序设计:GPU编程指南> 基本信息 原书名:CUDA Programming:A Developer’s Guide to Parallel Computing with ...
- C++对象模型笔记之程序设计模型
C++程序设计模型支持三种程序设计模型 1.程序模型(procedural model) 可以理解为过程化模型,就像C一样 2.抽象数据类型模型(ADT) 数据结构教材里有说过,查了下资料也不是很明确 ...
- PL真有意思(八):其它程序设计模型
前言 在之前几篇我们讨论的语法.语义.命名.类型和抽象适用于所有语言.然而我们的注意力都主要集中在命令式语言上,现在这篇来看看其它范式的语言.函数式和逻辑式语言是最主要的非命令式语言. 函数式语言 命 ...
- 【并行计算-CUDA开发】CUDA并行存储模型
CUDA并行存储模型 CUDA将CPU作为主机(Host),GPU作为设备(Device).一个系统中可以有一个主机和多个设备.CPU负责逻辑性强的事务处理和串行计算,GPU专注于执行高度线程化的并行 ...
- CUDA并行计算 | 线程模型与内存模型
文章目录 前言 CUDA线程模型(如何组织线程) CUDA内存模型(了解不同内存优缺点,合理使用) 前言 CUDA(Compute Unified Device Architecture)是显卡厂 ...
- CUDA并行存储模型
CUDA将CPU作为主机(Host),GPU作为设备(Device).一个系统中可以有一个主机和多个设备.CPU负责逻辑性强的事务处理和串行计算,GPU专注于执行高度线程化的并行处理任务.它们拥有相互 ...
- CUDA并行程序设计 开发环境搭建与远程调试
课题需要用到GPU加速.目前使用的台式电脑只有核心显卡,而实验室有一台服务器装有NVIDIA GTX980独显.因此,想搭建一个CUDA的开发环境,来实现在台式机上面开发cuda程序,程序在服务器而不 ...
- 【CUDA 基础】3.1 CUDA执行模型概述
title: [CUDA 基础]3.1 CUDA执行模型概述 categories: CUDA Freshman tags: CUDA SM SIMT SIMD Fermi Kepler toc: t ...
随机推荐
- dex文件格式学习
一.dex文件的生成 我们可以通过java文件来生成一个简单的dex文件 编译过程: 首先编写java代码如下: (1) 编译成 java class 文件 执行命令 : javac Hello.ja ...
- POJ3614奶牛晒阳光DINIC或者贪心
题意: n个区间,m种点,每种点有ci个,如果一个点的范围在一个区间上,那么就可以消耗掉一个区间,问最多可以消耗多少个区间,就是这n个区间中,有多少个可能被抵消掉. 思路: 方 ...
- Linux-鸟菜-1-Linux简介
Linux-鸟菜-1-Linux简介 鸟哥这一章是介绍了Linux的由来以及建议怎么学Linux.很多也是建议性的东西,概念性的东西(当然我不是排斥概念).把最后鸟哥的汇总粘过来吧. 建议大家去看下这 ...
- Linux-鸟菜-5-文件权限
Linux-鸟菜-5-文件权限 Linux作为多用户多任务的操作系统,文件权限以及目录配置相对于Windows的话应该更重要更细致一些.Linux一般将文件可存取的身份分为三个类别,分别是owner/ ...
- 前端Excel表格导入导出,包括合并单元格,表格自定义样式等
表格数据导入 读取导入Excel表格数据这里采用的是 xlsx 插件 npm i xlsx 读取excel需要通过 XLSX.read(data, {type: type}) 方法来实现,返回一个叫W ...
- 使用花生壳、瑞友天翼远程访问金蝶K3
金蝶版本号 WISE15.1 瑞友6.0系列 花生壳5 金蝶软件无法通过外网直接访问 因此需要使用瑞友天翼来实现远程访问 一般来说 金蝶服务器固定了IP地址以后 通过路由器开房两个外网端口即可实现瑞友 ...
- 19 常用API
API 什么是API? API (Application Programming Interface) :应用程序编程接口 简单来说:就是Java帮我们已经写好的一些方法,我们直接拿过来用就可以了 1 ...
- [bug] conda:Segmentation fault (core dumped)
参考 https://www.jianshu.com/p/5e230ef8a14d
- zabbix学习笔记:zabbix监控之短信报警
zabbix学习笔记:zabbix监控之短信报警 zabbix的报警方式有多种,除了常见的邮件报警外,特殊情况下还需要设置短信报警和微信报警等额外方式.本篇文章向大家介绍短信报警. 短信报警设置 短信 ...
- Linux服务之nginx服务篇四(配置https协议访问)
一.配置nginx支持https协议访问 编译安装nginx的时候需要添加相应的模块--with-http_ssl_module和--with-http_gzip_static_module(可通过/ ...