描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111630616
目 录
译者序
前言
关于作者
第一部分 理解CPU的并行性
第1章 CPU并行编程概述 2
1.1 并行编程的演化 2
1.2 核心越多,并行性越高 3
1.3 核心与线程 4
1.3.1 并行化更多的是线程还是核心 5
1.3.2 核心资源共享的影响 6
1.3.3 内存资源共享的影响 6
1.4 第一个串行程序 7
1.4.1 理解数据传输速度 8
1.4.2 imflip.c中的main( )函数 9
1.4.3 垂直翻转行:FlipImageV( ) 10
1.4.4 水平翻转列:FlipImageH( ) 11
1.5 程序的编辑、编译、运行 12
1.5.1 选择编辑器和编译器 12
1.5.2 在Windows 7、8、10平台上开发 12
1.5.3 在Mac平台上开发 14
1.5.4 在Unix平台上开发 14
1.6 Unix速成 15
1.6.1 与目录相关的Unix命令 15
1.6.2 与文件相关的Unix命令 16
1.7 调试程序 19
1.7.1 gdb 19
1.7.2 古典调试方法 20
1.7.3 valgrind 22
1.8 第一个串行程序的性能 22
1.8.1 可以估计执行时间吗 23
1.8.2 代码执行时OS在做什么 23
1.8.3 如何并行化 24
1.8.4 关于资源的思考 25
第2章 开发第一个CPU并行程序 26
2.1 第一个并行程序 26
2.1.1 imflipP.c中的main( )函数 27
2.1.2 运行时间 28
2.1.3 imflipP.c中main( )函数代码的划分 28
2.1.4 线程初始化 30
2.1.5 创建线程 31
2.1.6 线程启动/执行 32
2.1.7 线程终止(合并) 33
2.1.8 线程任务和数据划分 34
2.2 位图文件 35
2.2.1 BMP是一种无损/不压缩的文件格式 35
2.2.2 BMP图像文件格式 36
2.2.3 头文件ImageStuff.h 37
2.2.4 ImageStuff.c中的图像操作函数 38
2.3 执行线程任务 40
2.3.1 启动线程 41
2.3.2 多线程垂直翻转函数MTFlipV( ) 43
2.3.3 FlipImageV( )和MTFlipV( )的比较 46
2.3.4 多线程水平翻转函数MTFlipH(?) 47
2.4 多线程代码的测试/计时 49
第3章 改进第一个CPU并行程序 51
3.1 程序员对性能的影响 51
3.2 CPU对性能的影响 52
3.2.1 按序核心与乱序核心 53
3.2.2 瘦线程与胖线程 55
3.3 imf?lipP的性能 55
3.4 操作系统对性能的影响 56
3.4.1 创建线程 57
3.4.2 线程启动和执行 57
3.4.3 线程状态 58
3.4.4 将软件线程映射到硬件线程 59
3.4.5 程序性能与启动的线程 60
3.5 改进imf?lipP 61
3.5.1 分析MTFlipH( )中的内存访问模式 62
3.5.2 MTFlipH( )的多线程内存访问 63
3.5.3 DRAM访问的规则 64
3.6 imf?lipPM:遵循DRAM的规则 65
3.6.1 imflipP的混乱内存访问模式 65
3.6.2 改进imflipP的内存访问模式 65
3.6.3 MTFlipHM( ):内存友好的MTFlipH( ) 66
3.6.4 MTFlipVM( ):内存友好的MTFlipV( ) 69
3.7 imflipPM.C的性能 69
3.7.1 imflipP.c和imflipPM.c的性能比较 70
3.7.2 速度提升:MTFlipV( )与MTFlipVM( ) 71
3.7.3 速度提升:MTFlipH( )与MTFlipHM( ) 71
3.7.4 理解加速:MTFlipH( )与MTFlipHM( ) 71
3.8 进程内存映像 72
3.9 英特尔MIC架构:Xeon Phi 74
3.10 GPU是怎样的 75
3.11 本章小结 76
第4章 理解核心和内存 77
4.1 曾经的英特尔 77
4.2 CPU和内存制造商 78
4.3 动态存储器与静态存储器 79
4.3.1 静态随机存取存储器(SRAM) 79
4.3.2 动态随机存取存储器(DRAM) 79
4.3.3 DRAM接口标准 79
4.3.4 DRAM对程序性能的影响 80
4.3.5 SRAM对程序性能的影响 81
4.4 图像旋转程序:imrotate.c 81
4.4.1 imrotate.c的说明 82
4.4.2 imrotate.c:参数限制和简化 82
4.4.3 imrotate.c:实现原理 83
4.5 imrotate的性能 87
4.5.1 线程效率的定性分析 87
4.5.2 定量分析:定义线程效率 87
4.6 计算机的体系结构 89
4.6.1 核心、L1$和L2$ 89
4.6.2 核心内部资源 90
4.6.3 共享L3高速缓存(L3 $) 91
4.6.4 内存控制器 92
4.6.5 主存 92
4.6.6 队列、非核心和I/O 93
4.7 imrotateMC:让imrotate更高效 94
4.7.1 Rotate2( ):平方根和浮点除法有多差 96
4.7.2 Rotate3( )和Rotate4( ):sin( )和cos( )有多差 97
4.7.3 Rotate5( ):整数除法/乘法有多差 98
4.7.4 Rotate6( ):合并计算 100
4.7.5 Rotate7( ):合并更多计算 100
4.7.6 imrotateMC的总体性能 101
4.8 本章小结 103
第5章 线程管理和同步 104
5.1 边缘检测程序:imedge.c 104
5.1.1 imedge.c的说明 105
5.1.2 imedge.c:参数限制和简化 106
5.1.3 imedge.c:实现原理 106
5.2 imedge.c:实现 108
5.2.1 初始化和时间戳 109
5.2.2 不同图像表示的初始化函数 110
5.2.3 启动和终止线程 111
5.2.4 高斯滤波 112
5.2.5 Sobel 113
5.2.6 阈值过滤 114
5.3 imedge的性能 115
5.4 imedgeMC:让imedge更高效 116
5.4.1 利用预计算降低带宽 116
5.4.2 存储预计算的像素值 117
5.4.3 预计算像素值 118
5.4.4 读取图像并预计算像素值 119
5.4.5 PrGaussianFilter 1
前言
关于作者
第一部分 理解CPU的并行性
第1章 CPU并行编程概述 2
1.1 并行编程的演化 2
1.2 核心越多,并行性越高 3
1.3 核心与线程 4
1.3.1 并行化更多的是线程还是核心 5
1.3.2 核心资源共享的影响 6
1.3.3 内存资源共享的影响 6
1.4 第一个串行程序 7
1.4.1 理解数据传输速度 8
1.4.2 imflip.c中的main( )函数 9
1.4.3 垂直翻转行:FlipImageV( ) 10
1.4.4 水平翻转列:FlipImageH( ) 11
1.5 程序的编辑、编译、运行 12
1.5.1 选择编辑器和编译器 12
1.5.2 在Windows 7、8、10平台上开发 12
1.5.3 在Mac平台上开发 14
1.5.4 在Unix平台上开发 14
1.6 Unix速成 15
1.6.1 与目录相关的Unix命令 15
1.6.2 与文件相关的Unix命令 16
1.7 调试程序 19
1.7.1 gdb 19
1.7.2 古典调试方法 20
1.7.3 valgrind 22
1.8 第一个串行程序的性能 22
1.8.1 可以估计执行时间吗 23
1.8.2 代码执行时OS在做什么 23
1.8.3 如何并行化 24
1.8.4 关于资源的思考 25
第2章 开发第一个CPU并行程序 26
2.1 第一个并行程序 26
2.1.1 imflipP.c中的main( )函数 27
2.1.2 运行时间 28
2.1.3 imflipP.c中main( )函数代码的划分 28
2.1.4 线程初始化 30
2.1.5 创建线程 31
2.1.6 线程启动/执行 32
2.1.7 线程终止(合并) 33
2.1.8 线程任务和数据划分 34
2.2 位图文件 35
2.2.1 BMP是一种无损/不压缩的文件格式 35
2.2.2 BMP图像文件格式 36
2.2.3 头文件ImageStuff.h 37
2.2.4 ImageStuff.c中的图像操作函数 38
2.3 执行线程任务 40
2.3.1 启动线程 41
2.3.2 多线程垂直翻转函数MTFlipV( ) 43
2.3.3 FlipImageV( )和MTFlipV( )的比较 46
2.3.4 多线程水平翻转函数MTFlipH(?) 47
2.4 多线程代码的测试/计时 49
第3章 改进第一个CPU并行程序 51
3.1 程序员对性能的影响 51
3.2 CPU对性能的影响 52
3.2.1 按序核心与乱序核心 53
3.2.2 瘦线程与胖线程 55
3.3 imf?lipP的性能 55
3.4 操作系统对性能的影响 56
3.4.1 创建线程 57
3.4.2 线程启动和执行 57
3.4.3 线程状态 58
3.4.4 将软件线程映射到硬件线程 59
3.4.5 程序性能与启动的线程 60
3.5 改进imf?lipP 61
3.5.1 分析MTFlipH( )中的内存访问模式 62
3.5.2 MTFlipH( )的多线程内存访问 63
3.5.3 DRAM访问的规则 64
3.6 imf?lipPM:遵循DRAM的规则 65
3.6.1 imflipP的混乱内存访问模式 65
3.6.2 改进imflipP的内存访问模式 65
3.6.3 MTFlipHM( ):内存友好的MTFlipH( ) 66
3.6.4 MTFlipVM( ):内存友好的MTFlipV( ) 69
3.7 imflipPM.C的性能 69
3.7.1 imflipP.c和imflipPM.c的性能比较 70
3.7.2 速度提升:MTFlipV( )与MTFlipVM( ) 71
3.7.3 速度提升:MTFlipH( )与MTFlipHM( ) 71
3.7.4 理解加速:MTFlipH( )与MTFlipHM( ) 71
3.8 进程内存映像 72
3.9 英特尔MIC架构:Xeon Phi 74
3.10 GPU是怎样的 75
3.11 本章小结 76
第4章 理解核心和内存 77
4.1 曾经的英特尔 77
4.2 CPU和内存制造商 78
4.3 动态存储器与静态存储器 79
4.3.1 静态随机存取存储器(SRAM) 79
4.3.2 动态随机存取存储器(DRAM) 79
4.3.3 DRAM接口标准 79
4.3.4 DRAM对程序性能的影响 80
4.3.5 SRAM对程序性能的影响 81
4.4 图像旋转程序:imrotate.c 81
4.4.1 imrotate.c的说明 82
4.4.2 imrotate.c:参数限制和简化 82
4.4.3 imrotate.c:实现原理 83
4.5 imrotate的性能 87
4.5.1 线程效率的定性分析 87
4.5.2 定量分析:定义线程效率 87
4.6 计算机的体系结构 89
4.6.1 核心、L1$和L2$ 89
4.6.2 核心内部资源 90
4.6.3 共享L3高速缓存(L3 $) 91
4.6.4 内存控制器 92
4.6.5 主存 92
4.6.6 队列、非核心和I/O 93
4.7 imrotateMC:让imrotate更高效 94
4.7.1 Rotate2( ):平方根和浮点除法有多差 96
4.7.2 Rotate3( )和Rotate4( ):sin( )和cos( )有多差 97
4.7.3 Rotate5( ):整数除法/乘法有多差 98
4.7.4 Rotate6( ):合并计算 100
4.7.5 Rotate7( ):合并更多计算 100
4.7.6 imrotateMC的总体性能 101
4.8 本章小结 103
第5章 线程管理和同步 104
5.1 边缘检测程序:imedge.c 104
5.1.1 imedge.c的说明 105
5.1.2 imedge.c:参数限制和简化 106
5.1.3 imedge.c:实现原理 106
5.2 imedge.c:实现 108
5.2.1 初始化和时间戳 109
5.2.2 不同图像表示的初始化函数 110
5.2.3 启动和终止线程 111
5.2.4 高斯滤波 112
5.2.5 Sobel 113
5.2.6 阈值过滤 114
5.3 imedge的性能 115
5.4 imedgeMC:让imedge更高效 116
5.4.1 利用预计算降低带宽 116
5.4.2 存储预计算的像素值 117
5.4.3 预计算像素值 118
5.4.4 读取图像并预计算像素值 119
5.4.5 PrGaussianFilter 1
前 言
我经历过在IBM大型机上编写汇编语言来开发高性能程序的日子。用穿孔卡片编写程序,编译需要一天时间;你要留下在穿孔卡片上编写的程序,第二天再来拿结果。如果出现错误,你需要重复这些操作。在那些日子里,一位优秀的程序员必须理解底层的机器硬件才能编写出好的代码。当我看到现在的计算机科学专业的学生只学习抽象层次较高的内容以及像Ruby这样的语言时,我总会感到有些焦虑。尽管抽象是一件好事,因为它可以避免由于不必要的细节而使程序开发陷入困境,但当你尝试开发高性能代码时,抽象就变成了一件坏事。
自第一个CPU出现以来,计算机架构师在CPU硬件中添加了令人难以置信的功能来“容忍”糟糕的编程技巧。20年前,你必须手动设置机器指令的执行顺序,而如今在硬件中CPU会为你做这些(例如,乱序执行)。在GPU世界中也能清晰地看到类似的趋势。由于GPU架构师正在改进硬件功能,5年前我们在GPU编程中学习的大多数性能提升技术(例如,线程发散、共享存储体冲突以及减少原子操作的使用)正变得与改进的GPU架构越来越不相关,甚至5~10年后,即使是一名非常马虎的程序员,这些因素也会变得无关紧要。当然,这只是一个猜测。GPU架构师可以做的事取决于晶体管总数及客户需求。当说晶体管总数时,是指GPU制造商可以将多少个晶体管封装到集成电路(IC)即“芯片”中。当说客户需求时,是指即使GPU架构师能够实现某个功能,但如果客户使用的应用程序不能从中受益,就意味着浪费了部分的晶体管数量。
从编写教科书的角度出发,我考虑了所有的因素,逐渐明确讲授GPU编程的最佳方式是说明不同系列GPU(如Fermi、Kepler、Maxwell和Pascal)之间的不同并指明发展趋势,这可以让读者准备好迎接即将到来的下一代GPU,再下一代,……我会重点强调那些相对来说会长期存在的概念,同时也关注那些与平台相关的概念。也就是说,GPU编程完全关乎性能,如果你了解程序运行的平台架构,编写出了与平台相关的代码,就可以获得更高的性能。所以,提供平台相关的解释与通用的GPU概念一样有价值。本书内容的设计方式是,越靠后的章节,内容越具有平台特定性。
我认为本书最独特的地方就是通过第一部分中的CPU多线程来解释并行。第二部分介绍了GPU的大规模并行(与CPU的并行不同)。由于第一部分解释了CPU并行的方式,因此读者在第二部分中可以较为容易地理解GPU的并行。在过去的6年中,我设计了这种方法来讲授GPU编程,认识到从未学过并行编程课程的学生并不是很清楚大规模并行的概念。与GPU相比,“并行化任务”的概念在CPU架构中更容易理解。
本书的组织如下。第一部分(第1章至第5章)使用一些简单的程序来演示如何将大任务分成多个并行的子任务并将它们映射到CPU线程,分析了同一任务的多种并行实现方式,并根据计算核心和存储单元操作来研究这些方法的优缺点。本书的第二部分(第6章至第11章)将同一个程序在多个Nvidia GPU平台(Fermi、Kepler、Maxwell和Pascal)上并行化,并进行性能分析。由于CPU和GPU的核心和内存结构不同,分析结果的差异有时很有趣,有时与直觉相反。本书指出了这些结果的不同之处,并讨论了如何让GPU代码运行得更快。本书的最终目标是让程序员了解所有的做法,这样他们就可以应用好的做法,并避免将不好的做法应用到项目中。
尽管第一部分和第二部分已经完全涵盖了编写一个好的CUDA程序需要的所有内容,但总会有更多需要了解的东西。本书的第三部分为希望拓宽视野的读者指明了方向。第三部分并不是相关主题的详细参考文档,只是给出了一些入门介绍,读者可以从中获得学习这些内容的动力。这部分主要介绍了一些流行的CUDA库,比如cuBLAS、cuFFT、Nvidia Performance Primitives和Thrust(第12章);OpenCL编程语言(第13章);使用其他编程语言和API库进行GPU编程,包括Python、Metal、Swift、OpenGL、OpenGL ES、OpenCV和微软HLSL(第14章);深度学习库cuDNN(第15章)。
书中代码的下载地址为:https://www.crcpress.com/GPU-Parallel-ProgramDevelopment-Using- CUDA /Soyata/p/book/9781498750752。
自第一个CPU出现以来,计算机架构师在CPU硬件中添加了令人难以置信的功能来“容忍”糟糕的编程技巧。20年前,你必须手动设置机器指令的执行顺序,而如今在硬件中CPU会为你做这些(例如,乱序执行)。在GPU世界中也能清晰地看到类似的趋势。由于GPU架构师正在改进硬件功能,5年前我们在GPU编程中学习的大多数性能提升技术(例如,线程发散、共享存储体冲突以及减少原子操作的使用)正变得与改进的GPU架构越来越不相关,甚至5~10年后,即使是一名非常马虎的程序员,这些因素也会变得无关紧要。当然,这只是一个猜测。GPU架构师可以做的事取决于晶体管总数及客户需求。当说晶体管总数时,是指GPU制造商可以将多少个晶体管封装到集成电路(IC)即“芯片”中。当说客户需求时,是指即使GPU架构师能够实现某个功能,但如果客户使用的应用程序不能从中受益,就意味着浪费了部分的晶体管数量。
从编写教科书的角度出发,我考虑了所有的因素,逐渐明确讲授GPU编程的最佳方式是说明不同系列GPU(如Fermi、Kepler、Maxwell和Pascal)之间的不同并指明发展趋势,这可以让读者准备好迎接即将到来的下一代GPU,再下一代,……我会重点强调那些相对来说会长期存在的概念,同时也关注那些与平台相关的概念。也就是说,GPU编程完全关乎性能,如果你了解程序运行的平台架构,编写出了与平台相关的代码,就可以获得更高的性能。所以,提供平台相关的解释与通用的GPU概念一样有价值。本书内容的设计方式是,越靠后的章节,内容越具有平台特定性。
我认为本书最独特的地方就是通过第一部分中的CPU多线程来解释并行。第二部分介绍了GPU的大规模并行(与CPU的并行不同)。由于第一部分解释了CPU并行的方式,因此读者在第二部分中可以较为容易地理解GPU的并行。在过去的6年中,我设计了这种方法来讲授GPU编程,认识到从未学过并行编程课程的学生并不是很清楚大规模并行的概念。与GPU相比,“并行化任务”的概念在CPU架构中更容易理解。
本书的组织如下。第一部分(第1章至第5章)使用一些简单的程序来演示如何将大任务分成多个并行的子任务并将它们映射到CPU线程,分析了同一任务的多种并行实现方式,并根据计算核心和存储单元操作来研究这些方法的优缺点。本书的第二部分(第6章至第11章)将同一个程序在多个Nvidia GPU平台(Fermi、Kepler、Maxwell和Pascal)上并行化,并进行性能分析。由于CPU和GPU的核心和内存结构不同,分析结果的差异有时很有趣,有时与直觉相反。本书指出了这些结果的不同之处,并讨论了如何让GPU代码运行得更快。本书的最终目标是让程序员了解所有的做法,这样他们就可以应用好的做法,并避免将不好的做法应用到项目中。
尽管第一部分和第二部分已经完全涵盖了编写一个好的CUDA程序需要的所有内容,但总会有更多需要了解的东西。本书的第三部分为希望拓宽视野的读者指明了方向。第三部分并不是相关主题的详细参考文档,只是给出了一些入门介绍,读者可以从中获得学习这些内容的动力。这部分主要介绍了一些流行的CUDA库,比如cuBLAS、cuFFT、Nvidia Performance Primitives和Thrust(第12章);OpenCL编程语言(第13章);使用其他编程语言和API库进行GPU编程,包括Python、Metal、Swift、OpenGL、OpenGL ES、OpenCV和微软HLSL(第14章);深度学习库cuDNN(第15章)。
书中代码的下载地址为:https://www.crcpress.com/GPU-Parallel-ProgramDevelopment-Using- CUDA /Soyata/p/book/9781498750752。
Tolga Soyata
评论
还没有评论。