《高性能CUDA应用设计与开发》--笔记
第一章
- 数据并行C++ Thrust API
- 可用于C或者C++的Runtime API
- 可用于C或者C++的Driver API
以上API自高层向低层。Thrust API 具有较高可读性、可维护性,并且提供了很多方法(如归约),但它与硬件相隔离,从而无法发挥硬件的全部功能;CUDA Runtime 使得C语言语法扩展,来获得GPGPU的所有可编程特性;Driver API 可以更加细致的控制,且不局限于队列和数据的传输。
- 用于CUDA的GPU是安装于主机系统(Host)的独立设备,主处理器和所有的GPGPU可以同时处理各自的计算任务。
- 传输方式:cudaMemcpu()显示传输,锁定页内存映射的隐式传输(可以实现零拷贝操作),最底层通过设备驱动程序的软件模块进行交互。
- GPGPU运行在一个和主处理器相隔离的存储空间中。这些存储比传统的主机内存(8~20GB/s)有更大的带宽 (160~200GB/s)
- CUDA kernel是可以在主机代码中调用而在CUDA设备上运行的子程序。kernel没有返回值,通过__global__来定义,可以暂时理解为GPU的main函数。
- Kernel调用是异步的,主机仅仅把要执行的kernel顺序提交给GPGPU,并不等待其完成,然后直接处理后面的任务。
- 由于kernel异步执行,为提高效率,可以创建一个kernel组成的流水线,使得GPGPU尽可能长时间保持忙碌;CUDA提供的同步方式:显示调用cudaThreadSynchronize()和cudaMemcpy()。
- GPU上的基本运行单位是线程。每个线程在运行时都好像独占一个处理器,并同时运行于共享内存环境中。一个kernel利用多个线程完成任务称为线程级并行(TLP),有别于处理器指令间的指令级并行(ILP)
- GPU上最大可共享的内存区域称为全局内存,它是遵循整合访问的硬件,通常对连续的128字节数据块进行内存访问。但其访问速度在GPU中最慢,最快的访问是通过寄存器来进行访问。
- CUDA提供了简单的C语言扩展,使得线程可以通过CUDA共享内存空间(shared memory)或通过院子内存操作(atomic)来通信。
- 环境配置不仅定义执行kernel所需的线程数量,还包括grid网格中维度的分配。
1.5 理解首个Runtime kernel
- 将数据放入并始终存储于GPGPU:GPU全局内存带宽比主机带宽快20倍。
- 交给GPGPU足够多的任务:防止启动kernel的时间所占比重过大。
- 注重GPGPU上的数据重用,以避免带宽限制。
1.7 大O记号的思想与数据传输:
- O(1),无论输入集尺寸如何,算法总消耗固定的资源,具有固定的执行时间。如利用GPU索引数组的元素。
- O(n),资源消耗随着输入尺寸增长成线性增长,如循环访问数据集的算法。
- O(n2),平方比例关系,还可能有n的三次方,n的四次方等
BLAS:基本线性代数子程序集,由三层运算级别构成:
- 级别1:向量-向量操作,数据复杂度O(n),计算复杂度O(n);如两个向量的内积。
- 级别2:矩阵-向量操作,数据复杂度O(n2),计算复杂度O(n2);矩阵与向量乘积。
- 级别3:矩阵-向量操作,数据复杂度O(n2),计算复杂度O(n3);密集矩阵乘法。
*提高性能的方案:创建一个流水线整合大量简单的计算密集型任务或者将多个低密度的操作合成一个单一的仿函数或者kernel。
第二章 CUDA在机器学习与优化中的应用
- 基于第一性原理分析及其他方法的人工推导模型。
- 基于数据拟合的参数化模型。(如神经网络)
第四章 CUDA执行模型
- 可以透明得扩展到任意数量的SM上
- 对SM的位置没有限制
- 能够将执行中的Kernel与用户参数广播给硬件。*并行广播是扩展性最强、速度最快的通信机制。
4.1.1 线程调度:通过执行配置统筹性能与并行度。
- 当前不需要等待数据从设备内存中传来。
- 当前不需要等待前一条指令的完成。
每个SM处理器有32个单指令多数据(SIMD)处理核心,使用SIMD意味着SM中调度器要求其控制的所有处理核心执行相同的指令,每个处理核心可以有不同的数据,warp是SM内部的基本调度单元。每个时钟周期执行两次操作:选择两个warp并为每个warp发射一条指令,每个warp在16个处理核心、16个加载/储存单元或者4个SFU上执行。
- 使用不同的算法重新规划问题
- 将不同计算代价的任务分别列表,每个列表使用一个kernel函数
- 将计算任务排序并分块,块的大小为半个warp的整数倍
- 利用异步kernel执行方式
- 使用主处理器来执行分任务
- 提升占用率
- 最大化可用的寄存器(让编译器使用__launch__bounds__给每个kernel函数分配额外的寄存器)
- 调整线程块的维度以最好的利用SM Warp调度器
- 修改代码以利用ILP是每个线程处理多个元素
- 不要把工作都塞到一个kernel中:
- 尝试创建小线程块,是操作密度均匀
- 不要讲相同类型的操作都聚集到kernel函数的一部分中
- 不要在一个功能单元上出现瓶颈
第五章 CUDA储存器
- 时间局部性:假设当前访问的数据在不远的将来可能会被再次访问,如满足LRU算法
- 空间局部性:缓存邻近的数据,如渲染操作。
- L1缓存是基于空间重用而非时间重用设计
- L1缓存并不会影响全局内存的写操作,这些操作会越过L1缓存。
- L1缓存并不保证一致性
- L1缓存有10~20时钟周期的延迟
- 具有良好的可配置性,可以将其设置为动态缓存和共享缓存
- 在kernel中以静态的方式或在文件中以全局变量进行声明。如在kernel函数中调用带有__shared__标识的函数。
- 通过Drived API的函数 cuFuncSetSharedSize在kernel中动态声明。
- 通过执行配置动态声明。
- 数据存放与全局内存
- 数据在kernel中是只读的(可以使用const来限制)
- 数据访问与线程ID无关
常量内存静态申请,主机代码只有通过Runtime API中的cudaGetSymbolAddress(), cudaGetSymbolSize(), cudaMemcpyToSymbol()等才能往常量内存中写入数据。
其通常可以绑定全局内存的数据,并具有一定的缓存功能,主要特征如下:
- 通常用于可视化处理
- 基于2D空间局部性的纹理内存可对缓存进行优化
- 每个SM有8K的缓存空间
- 可以高效的拆解和广播数据
- 具有9位数据计算单元,可以进行越界数据访问处理、插值以及整型到浮点型的转换。
*区分将纹理内存绑定到cudaMalloc()申请的内存和cudaMallocPitch()申请的线性对齐内存
- 内存地址是对齐的,要保证合适的线程块尺寸(16的倍数),在定义结构体的时候使用限定符__align__(8), __align__(16)
- warp内各个线程所访问的数据在一个连续的区域。
为了最大限度的利用全局内存子系统,运行时可以尝试每个线程处理多个元素,是过个内存操作流水化进行(ILP角度考虑),启动足够多的线程以最大化吞吐量(TLP角度考虑)。
第六章 高效使用CUDA存储器
第七章 提高并行度的技巧
- 锁存页主机内存的分配
- 设备内存分配
- 设备内存设置
- 设备到设备的内存拷贝
- L1缓存与共享内存之间的配置转换
__global__ void fillKernel(int * a, int n, int offset)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if(tid < n)
{
for(int i = 0; i < 100; ++i)
{
a[tid] = offset + tid;
}
}
}
int main(int argc, char * agrv[])
{
int nGPU;
int n = 1000000;
int size = n * sizeof(int);
cudaGetDeviceCount(&nGPG);
int *d_A[nGPU];
for(int i = 0; i < nGPU; i++)
{
cudaSetDevice(i);
cudaMalloc(&d_A[i], size);
}
int * h_A;
cudaHostAlloc(&h_A, nGPU * sizeof(int), cudaHostAllocPortable);
for(int i = 0; i < nGPU; i++)
{
int nThreadsPerBlock = 512;
int nBlocks = n / nThreadsPerBlock + ((n % nThreadsPerBlock) ? 1 : 0);
cudaSetDevice(i);
fillKernel<<<nBlocks, nThreadsPerBlock>>>(d_A[i], n, i * n);
cudaMemcpyAsync(&h_A[i * n], d_A[i], size, cudaMemcpyDeviceToHost);
}
cudaDeviceSynchronize();
//检查正确性
for(int i = 0; i < nGPU * n; i++)
{
if(h_A[i] != i)
{
printf("Error h_A[%d] = %d\n", i, h_A[i]);
exit(1);
}
}
printf("Success.\n");
cudaFreeHost(h_A);
for(int i = 0; i < nGPU; i++)
{
cudaSetDevice(i);
cudaFree(d_A[i]);
}
return 0 ;
}
- 所有独立操作应该在依赖操作之前执行
- 任何同步应该尽量延后
- 将主机内存映射到所有GPU设备的内存空间。
- 手动分配空间并传递数据。
- 对于将在分布式MPI(Message Passing Interface)环境中运行的程序,在各个设备上分配空间,并通过MIP发送与接收调用将数据传给GPU。
- 无需分割数据
- 无需在设备内存中分配空间或者手动拷贝数据
- 无需使用流将数据传输与kernel执行进行重叠
第八章 CUDA在所有GPU与CPU程序中的应用
第九章 CUDA与图形渲染混合编程
《高性能CUDA应用设计与开发》--笔记的更多相关文章
- HTML+CSS笔记 CSS笔记集合
HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...
- CSS笔记--选择器
CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...
- HTML+CSS笔记 CSS中级 一些小技巧
水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...
- HTML+CSS笔记 CSS中级 颜色&长度值
颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...
- HTML+CSS笔记 CSS中级 缩写入门
盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...
- HTML+CSS笔记 CSS进阶再续
CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...
- HTML+CSS笔记 CSS进阶续集
元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...
- HTML+CSS笔记 CSS进阶
文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...
- HTML+CSS笔记 CSS入门续集
继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...
- HTML+CSS笔记 CSS入门
简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...
随机推荐
- linux系统UDP的socket通信编程
发送方: /* * File: main.c * Author: tianshuai * * Created on 2011年11月29日, 下午10:34 * * 主要实现:发送20个文本消息,然后 ...
- 集群应用Session一致性实现的三种方案
转自:http://blog.csdn.net/zwx521515/article/details/78679679 https://www.cnblogs.com/study-everyday/p/ ...
- Java多线程详解(三)
1)死锁 两个线程相互等待对方释放同步监视器时会出现死锁的现象,这时所有的线程都处于阻塞状态,程序无法继续向下执行. 如下就是会出现死锁的程序. 首先flag = 1,线程d1开始执行,锁住对象o1, ...
- 最舒适的路(并查集+枚举)(hdu1598)
hdu1598 find the most comfortable road Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768 ...
- MyBatis DAO层传递参数到mapping.xml 几种方式
Dao层传递参数到mapping.xml文件的几种方式:(Mybatis传值总结) 第一种:传递单个参数 Dao层Code片段: /** * 根据articleId查询XXXX详情. * * @par ...
- 170515、mybatis批量操作
//Java代码 public void batchAdd(){ SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSession(); Stud ...
- 云计算之路-阿里云上:消灭“黑色n秒”第一招——不让CPU空闲
昨天对“黑色n秒”问题的最终猜想以失败而告终,从而让我们结束了被动猜想阶段,进入了主动进攻阶段——出招. 今天出第一招——用C#写个小程序,让其在每个CPU核上运行一个线程,不让任何一个CPU核进入空 ...
- 沈阳网络赛K-Supreme Number【规律】
26.89% 1000ms 131072K A prime number (or a prime) is a natural number greater than 11 that cannot be ...
- C/C++ 开放库
C/C++ 开放库 1.Best C/C++ Network Library 2.A list of open source C++ libraries
- Java字符编码问题
今天研究了一下,记录下来 中间用的是redis,可以使用任意其他的io替代,一样的 Test1 String s1 = "我要测试"; String s2 = "I wa ...