描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111743194
编辑推荐
·讨论如何利用MPI、Pthreads、OpenMP和CUDA这四种广泛使用的并行编程API来编写并行程序,其中,关于CUDA的章节是全新的。 ·新增关于GPU编程和异构编程的章节,此外,还新增了与并行程序开发相关的示例和练习。 ·学习曲线友好,从简单的编程示例开始,逐步构建更具挑战性的示例,关于四种API的章节相互独立,便于读者根据需要选择阅读。 ·配有丰富的练习和编程作业,所有源代码均可从本书网站免费下载。
内容简介
本书主要讲解如何使用MPI、Pthreads和OpenMP开发有效的并行程序,并在多核和集群架构上编译运行并行程序。本书第1版已经过广泛的本科教学实践,第2版做了细致的更新,清晰地阐释了如何设计、调试和评估分布式和共享内存程序的性能,并新增关于GPU编程和异构编程的内容,对加速器的讨论更加全面。第2版还对习题做了更新,读者可通过习题进一步熟悉并掌握编译、运行和修改示例程序的方法。
目 录
目 录
An Introduction to Parallel Programming, Second Edition
译者序
前言
第1章 为什么需要并行计算 1
1.1 为什么需要不断提高性能 1
1.2 为什么需要建立并行系统 2
1.3 为什么需要编写并行程序 2
1.4 如何编写并行程序 5
1.5 我们将做什么 6
1.6 并发、并行和分布式 7
1.7 本书其余部分 8
1.8 一点警告 8
1.9 排版惯例 9
1.10 小结 9
1.11 练习 10
第2章 并行硬件与并行软件 12
2.1 背景知识 12
2.1.1 冯·诺依曼体系结构 12
2.1.2 进程、多任务和线程 13
2.2 冯·诺依曼模型的改进 14
2.2.1 缓存基础 14
2.2.2 缓存映射 16
2.2.3 缓存和程序:示例 17
2.2.4 虚拟内存 18
2.2.5 指令级并行 19
2.2.6 硬件多线程 21
2.3 并行硬件 22
2.3.1 并行计算机的分类 22
2.3.2 SIMD系统 22
2.3.3 MIMD系统 24
2.3.4 互连网络 26
2.3.5 高速缓存一致性 30
2.3.6 共享内存与分布式内存 32
2.4 并行软件 33
2.4.1 注意事项 33
2.4.2 协调进程/线程 33
2.4.3 共享内存 34
2.4.4 分布式内存 37
2.4.5 GPU编程 40
2.4.6 混合系统编程 40
2.5 输入和输出 41
2.5.1 MIMD系统 41
2.5.2 GPU 41
2.6 性能 42
2.6.1 在MIMD系统中的加速比和
效率 42
2.6.2 阿姆达定律 44
2.6.3 MIMD系统的可扩展性 45
2.6.4 MIMD程序的计时 46
2.6.5 GPU性能 48
2.7 并行程序设计 49
2.7.1 示例 49
2.8 编写和运行并行程序 53
2.9 假设 53
2.10 小结 54
2.10.1 串行系统 54
2.10.2 并行硬件 55
2.10.3 并行软件 56
2.10.4 输入和输出 57
2.10.5 性能 57
2.10.6 并行程序设计 58
2.10.7 假设 58
2.11 练习 58
第3章 基于MPI的分布式
内存编程 62
3.1 入门 62
3.1.1 编译和执行 63
3.1.2 MPI程序 64
3.1.3 MPI_Init和
MPI_Finalize 64
3.1.4 通信域、MPI_Comm_size和MPI_Comm_rank 65
3.1.5 SPMD程序 66
3.1.6 通信 66
3.1.7 MPI_Send 66
3.1.8 MPI_Recv 67
3.1.9 消息匹配 68
3.1.10 status_p参数 69
3.1.11 MPI_Send和MPI_Recv的
语义 69
3.1.12 一些潜在的陷阱 70
3.2 MPI中的梯形法则 70
3.2.1 梯形法则 70
3.2.2 梯形法则的并行化 71
3.3 处理I/O 74
3.3.1 输出 74
3.3.2 输入 75
3.4 集合通信 76
3.4.1 树形结构的通信 76
3.4.2 MPI_Reduce 77
3.4.3 集合通信与点对点通信 78
3.4.4 MPI_Allreduce 79
3.4.5 广播 80
3.4.6 数据分布 82
3.4.7 分散 83
3.4.8 收集 84
3.4.9 综合实例 85
3.5 MPI派生的数据类型 88
3.6 MPI程序的性能评估 91
3.6.1 计时 91
3.6.2 结果 93
3.6.3 加速比和效率 95
3.6.4 可扩展性 95
3.7 一种并行排序算法 96
3.7.1 一些简单的串行排序算法 96
3.7.2 并行奇偶移项排序 98
3.7.3 MPI程序中的安全性 100
3.7.4 关于并行奇偶排序的一些
补充细节 102
3.8 小结 103
3.9 练习 106
3.10 编程作业 112
第4章 Pthreads共享内存编程 114
4.1 进程、线程和Pthreads 114
4.2 Hello, world 115
4.2.1 执行 116
4.2.2 预备 117
4.2.3 启动线程 117
4.2.4 运行线程 119
4.2.5 停止线程 120
4.2.6 错误检查 120
4.2.7 启动线程的其他方法 120
4.3 矩阵-向量乘法 121
4.4 临界区 123
4.5 忙等待 126
4.6 互斥锁 128
4.7 生产者-消费者同步和信号量 131
4.8 栅栏和条件变量 135
4.8.1 忙等待和互斥锁 135
4.8.2 信号量 136
4.8.3 条件变量 137
4.8.4 Pthreads栅栏 139
4.9 读写锁 139
4.9.1 排序的链表函数 140
4.9.2 多线程链表 142
4.9.3 Pthreads的读写锁 144
4.9.4 各种实现方案的性能 145
4.9.5 实现读写锁 146
4.10 缓存、缓存一致性和伪共享 147
4.11 线程安全 150
4.11.1 不正确的程序可以产生
正确的输出 153
4.12 小结 153
4.13 练习 154
4.14 编程作业 159
第5章 OpenMP共享内存编程 161
5.1 入门 162
5.1.1 编译和运行OpenMP程序 163
5.1.2 程序 163
5.1.3 错误检查 165
5.2 梯形法则 166
5.2.1 第一个OpenMP版本 166
5.3 变量的作用域 170
5.4 归约子句 170
5.5 parallel指令 173
5.5.1 注意事项 174
5.5.2 数据依赖性 175
5.5.3 寻找循环迭代相关 176
5.5.4 估算π 176
5.5.5 关于作用域的更多内容 178
5.6 关于OpenMP中的循环的更多
内容:排序 179
5.6.1 冒泡排序 179
5.6.2 奇偶移项排序 180
5.7 循环的调度 182
5.7.1 schedule子句 183
5.7.2 static调度类型 185
5.7.3 dynamic和guided调度
类型 185
5.7.4 runtime调度类型 186
5.7.5 哪种调度 187
5.8 生产者和消费者 188
5.8.1 队列 188
5.8.2 消息传递 188
5.8.3 发送消息 189
5.8.4 接收消息 189
5.8.5 终止检测 190
5.8.6 开始 190
5.8.7 atomic指令 191
5.8.8 临界区和锁 191
5.8.9 在消息传递程序中使用锁 193
5.8.10 critical指令、atomic
指令或锁 194
5.8.11 注意事项 194
5.9 缓存、缓存一致性和伪共享 195
5.10 任务化 199
5.11 线程安全 202
5.11.1 不正确的程序可以产生
正确的输出 204
5.12 小结 204
5.13 练习 208
5.14 编程作业 211
第6章 用CUDA进行GPU编程 215
6.1 GPU和GPGPU 215
6.2 GPU架构 215
6.3 异构计算 217
6.4 CUDA hello 217
6.4.1 源代码 218
6.4.2 编译与运行程序 219
6.5 深入了解 219
6.6 线程、线程块和线程网格 220
6.7 NVIDIA计算能力和设备架构 223
6.8 向量加法 223
6.8.1 核函数 224
6.8.2 Get_args函数 225
6.8.3 Allocate_vectors函数和
托管内存 226
6.8.4 main函数调用的其他函数 227
6.8.5 显式内存传输 229
6.9 从CUDA核函数返回结果 231
6.10 CUDA梯形法则I 233
6.10.1 梯形法则 233
6.10.2 一种CUDA实现 234
6.10.3 初始化、返回值和最后
更新 235
6.10.4 使用正确的线程 236
6.10.5 更新返回值和atomicAdd
函数 236
6.10.6 CUDA梯形法则的性能 237
6.11 CUDA梯形法则II:提升性能 238
6.11.1 树形通信 238
6.11.2 局部变量、寄存器、共享和
全局内存 239
6.11.3 线程束和线程束洗牌 240
6.11.4 使用线程束洗牌实现树形
全局求和 241
6.11.5 共享内存和线程束洗牌的
替代方案 242
6.12 用warpSize个线程块实现
梯形法则 243
6.12.1 主机代码 244
6.12.2 使用线程束洗牌的核函数 244
6.12.3 使用共享内存的核函数 244
6.12.4 性能 245
6.13 CUDA梯形法则III:使用具有
多个线程束的线程块 245
6.13.1 __syncthreads函数 246
6.13.2 关于共享内存的更多内容 247
6.13.3 使用共享内存的线程束
求和 247
6.13.4 共享内存库 248
6.13.5 收尾工作 249
6.13.6 性能 251
6.14 双调排序 251
6.14.1 串行双调排序 251
6.14.2 蝶式交换和二进制表示 254
6.14.3 并行双调排序I 256
6.14.4 并行双调排序II 258
6.14.5 CUDA双调排序的性能 259
6.15 小结 260
6.16 练习 264
6.17 编程作业 267
第7章 并行程序开发 269
7.1 两种n-body问题的解决方案 269
7.1.1 问题描述 269
7.1.2 两种串行方案 270
7.1.3 并行化n-body求解方案 274
7.1.4 关于I/O的说明 276
7.1.5 使用OpenMP并行化基本
求解方案 277
7.1.6 使用OpenMP并行化简化
求解方案 279
7.1.7 评估OpenMP代码 283
7.1.8 使用Pthreads并行化求解
方案 284
7.1.9 使用MPI并行化求解方案 284
7.1.10 使用MPI并行化简化求解
方案 286
7.1.11 MPI简化求解的性能 291
7.1.12 使用CUDA并行化基本
求解方案 292
7.1.13 关于CUDA协同组的说明 294
7.1.14 基本CUDA n-body求解
方案的性能 295
7.1.15 提高CUDA n-body求解
方案性能的方法 295
7.1.16 在n-body求解方案中使用
共享内存技术 296
7.2 样本排序 299
7.2.1 样本排序和桶排序 299
7.2.2 选择样本数据 300
7.2.3 Map函数的简单实现 301
7.2.4 Map的另一种实现方案 302
7.2.5 并行化样本排序 305
7.2.6 使用OpenMP实现样本
排序 308
7.2.7 使用Pthreads实现样本
排序 312
7.2.8 使用MPI实现样本排序 314
7.2.9 使用CUDA实现样本排序 323
7.3 注意事项 331
7.4 使用哪种API 331
7.5 小结 332
7.5.1 MPI 333
7.6 练习 334
7.7 编程作业 340
第8章 下一步该怎么走 343
参考文献 345
An Introduction to Parallel Programming, Second Edition
译者序
前言
第1章 为什么需要并行计算 1
1.1 为什么需要不断提高性能 1
1.2 为什么需要建立并行系统 2
1.3 为什么需要编写并行程序 2
1.4 如何编写并行程序 5
1.5 我们将做什么 6
1.6 并发、并行和分布式 7
1.7 本书其余部分 8
1.8 一点警告 8
1.9 排版惯例 9
1.10 小结 9
1.11 练习 10
第2章 并行硬件与并行软件 12
2.1 背景知识 12
2.1.1 冯·诺依曼体系结构 12
2.1.2 进程、多任务和线程 13
2.2 冯·诺依曼模型的改进 14
2.2.1 缓存基础 14
2.2.2 缓存映射 16
2.2.3 缓存和程序:示例 17
2.2.4 虚拟内存 18
2.2.5 指令级并行 19
2.2.6 硬件多线程 21
2.3 并行硬件 22
2.3.1 并行计算机的分类 22
2.3.2 SIMD系统 22
2.3.3 MIMD系统 24
2.3.4 互连网络 26
2.3.5 高速缓存一致性 30
2.3.6 共享内存与分布式内存 32
2.4 并行软件 33
2.4.1 注意事项 33
2.4.2 协调进程/线程 33
2.4.3 共享内存 34
2.4.4 分布式内存 37
2.4.5 GPU编程 40
2.4.6 混合系统编程 40
2.5 输入和输出 41
2.5.1 MIMD系统 41
2.5.2 GPU 41
2.6 性能 42
2.6.1 在MIMD系统中的加速比和
效率 42
2.6.2 阿姆达定律 44
2.6.3 MIMD系统的可扩展性 45
2.6.4 MIMD程序的计时 46
2.6.5 GPU性能 48
2.7 并行程序设计 49
2.7.1 示例 49
2.8 编写和运行并行程序 53
2.9 假设 53
2.10 小结 54
2.10.1 串行系统 54
2.10.2 并行硬件 55
2.10.3 并行软件 56
2.10.4 输入和输出 57
2.10.5 性能 57
2.10.6 并行程序设计 58
2.10.7 假设 58
2.11 练习 58
第3章 基于MPI的分布式
内存编程 62
3.1 入门 62
3.1.1 编译和执行 63
3.1.2 MPI程序 64
3.1.3 MPI_Init和
MPI_Finalize 64
3.1.4 通信域、MPI_Comm_size和MPI_Comm_rank 65
3.1.5 SPMD程序 66
3.1.6 通信 66
3.1.7 MPI_Send 66
3.1.8 MPI_Recv 67
3.1.9 消息匹配 68
3.1.10 status_p参数 69
3.1.11 MPI_Send和MPI_Recv的
语义 69
3.1.12 一些潜在的陷阱 70
3.2 MPI中的梯形法则 70
3.2.1 梯形法则 70
3.2.2 梯形法则的并行化 71
3.3 处理I/O 74
3.3.1 输出 74
3.3.2 输入 75
3.4 集合通信 76
3.4.1 树形结构的通信 76
3.4.2 MPI_Reduce 77
3.4.3 集合通信与点对点通信 78
3.4.4 MPI_Allreduce 79
3.4.5 广播 80
3.4.6 数据分布 82
3.4.7 分散 83
3.4.8 收集 84
3.4.9 综合实例 85
3.5 MPI派生的数据类型 88
3.6 MPI程序的性能评估 91
3.6.1 计时 91
3.6.2 结果 93
3.6.3 加速比和效率 95
3.6.4 可扩展性 95
3.7 一种并行排序算法 96
3.7.1 一些简单的串行排序算法 96
3.7.2 并行奇偶移项排序 98
3.7.3 MPI程序中的安全性 100
3.7.4 关于并行奇偶排序的一些
补充细节 102
3.8 小结 103
3.9 练习 106
3.10 编程作业 112
第4章 Pthreads共享内存编程 114
4.1 进程、线程和Pthreads 114
4.2 Hello, world 115
4.2.1 执行 116
4.2.2 预备 117
4.2.3 启动线程 117
4.2.4 运行线程 119
4.2.5 停止线程 120
4.2.6 错误检查 120
4.2.7 启动线程的其他方法 120
4.3 矩阵-向量乘法 121
4.4 临界区 123
4.5 忙等待 126
4.6 互斥锁 128
4.7 生产者-消费者同步和信号量 131
4.8 栅栏和条件变量 135
4.8.1 忙等待和互斥锁 135
4.8.2 信号量 136
4.8.3 条件变量 137
4.8.4 Pthreads栅栏 139
4.9 读写锁 139
4.9.1 排序的链表函数 140
4.9.2 多线程链表 142
4.9.3 Pthreads的读写锁 144
4.9.4 各种实现方案的性能 145
4.9.5 实现读写锁 146
4.10 缓存、缓存一致性和伪共享 147
4.11 线程安全 150
4.11.1 不正确的程序可以产生
正确的输出 153
4.12 小结 153
4.13 练习 154
4.14 编程作业 159
第5章 OpenMP共享内存编程 161
5.1 入门 162
5.1.1 编译和运行OpenMP程序 163
5.1.2 程序 163
5.1.3 错误检查 165
5.2 梯形法则 166
5.2.1 第一个OpenMP版本 166
5.3 变量的作用域 170
5.4 归约子句 170
5.5 parallel指令 173
5.5.1 注意事项 174
5.5.2 数据依赖性 175
5.5.3 寻找循环迭代相关 176
5.5.4 估算π 176
5.5.5 关于作用域的更多内容 178
5.6 关于OpenMP中的循环的更多
内容:排序 179
5.6.1 冒泡排序 179
5.6.2 奇偶移项排序 180
5.7 循环的调度 182
5.7.1 schedule子句 183
5.7.2 static调度类型 185
5.7.3 dynamic和guided调度
类型 185
5.7.4 runtime调度类型 186
5.7.5 哪种调度 187
5.8 生产者和消费者 188
5.8.1 队列 188
5.8.2 消息传递 188
5.8.3 发送消息 189
5.8.4 接收消息 189
5.8.5 终止检测 190
5.8.6 开始 190
5.8.7 atomic指令 191
5.8.8 临界区和锁 191
5.8.9 在消息传递程序中使用锁 193
5.8.10 critical指令、atomic
指令或锁 194
5.8.11 注意事项 194
5.9 缓存、缓存一致性和伪共享 195
5.10 任务化 199
5.11 线程安全 202
5.11.1 不正确的程序可以产生
正确的输出 204
5.12 小结 204
5.13 练习 208
5.14 编程作业 211
第6章 用CUDA进行GPU编程 215
6.1 GPU和GPGPU 215
6.2 GPU架构 215
6.3 异构计算 217
6.4 CUDA hello 217
6.4.1 源代码 218
6.4.2 编译与运行程序 219
6.5 深入了解 219
6.6 线程、线程块和线程网格 220
6.7 NVIDIA计算能力和设备架构 223
6.8 向量加法 223
6.8.1 核函数 224
6.8.2 Get_args函数 225
6.8.3 Allocate_vectors函数和
托管内存 226
6.8.4 main函数调用的其他函数 227
6.8.5 显式内存传输 229
6.9 从CUDA核函数返回结果 231
6.10 CUDA梯形法则I 233
6.10.1 梯形法则 233
6.10.2 一种CUDA实现 234
6.10.3 初始化、返回值和最后
更新 235
6.10.4 使用正确的线程 236
6.10.5 更新返回值和atomicAdd
函数 236
6.10.6 CUDA梯形法则的性能 237
6.11 CUDA梯形法则II:提升性能 238
6.11.1 树形通信 238
6.11.2 局部变量、寄存器、共享和
全局内存 239
6.11.3 线程束和线程束洗牌 240
6.11.4 使用线程束洗牌实现树形
全局求和 241
6.11.5 共享内存和线程束洗牌的
替代方案 242
6.12 用warpSize个线程块实现
梯形法则 243
6.12.1 主机代码 244
6.12.2 使用线程束洗牌的核函数 244
6.12.3 使用共享内存的核函数 244
6.12.4 性能 245
6.13 CUDA梯形法则III:使用具有
多个线程束的线程块 245
6.13.1 __syncthreads函数 246
6.13.2 关于共享内存的更多内容 247
6.13.3 使用共享内存的线程束
求和 247
6.13.4 共享内存库 248
6.13.5 收尾工作 249
6.13.6 性能 251
6.14 双调排序 251
6.14.1 串行双调排序 251
6.14.2 蝶式交换和二进制表示 254
6.14.3 并行双调排序I 256
6.14.4 并行双调排序II 258
6.14.5 CUDA双调排序的性能 259
6.15 小结 260
6.16 练习 264
6.17 编程作业 267
第7章 并行程序开发 269
7.1 两种n-body问题的解决方案 269
7.1.1 问题描述 269
7.1.2 两种串行方案 270
7.1.3 并行化n-body求解方案 274
7.1.4 关于I/O的说明 276
7.1.5 使用OpenMP并行化基本
求解方案 277
7.1.6 使用OpenMP并行化简化
求解方案 279
7.1.7 评估OpenMP代码 283
7.1.8 使用Pthreads并行化求解
方案 284
7.1.9 使用MPI并行化求解方案 284
7.1.10 使用MPI并行化简化求解
方案 286
7.1.11 MPI简化求解的性能 291
7.1.12 使用CUDA并行化基本
求解方案 292
7.1.13 关于CUDA协同组的说明 294
7.1.14 基本CUDA n-body求解
方案的性能 295
7.1.15 提高CUDA n-body求解
方案性能的方法 295
7.1.16 在n-body求解方案中使用
共享内存技术 296
7.2 样本排序 299
7.2.1 样本排序和桶排序 299
7.2.2 选择样本数据 300
7.2.3 Map函数的简单实现 301
7.2.4 Map的另一种实现方案 302
7.2.5 并行化样本排序 305
7.2.6 使用OpenMP实现样本
排序 308
7.2.7 使用Pthreads实现样本
排序 312
7.2.8 使用MPI实现样本排序 314
7.2.9 使用CUDA实现样本排序 323
7.3 注意事项 331
7.4 使用哪种API 331
7.5 小结 332
7.5.1 MPI 333
7.6 练习 334
7.7 编程作业 340
第8章 下一步该怎么走 343
参考文献 345
前 言
前 言
An Introduction to Parallel Programming, Second Edition
一段时间以来,并行硬件已经无处不在:很难找到不使用多核处理器的笔记本计算机、台式计算机或服务器。集群计算在今天几乎和20世纪90年代的高功率工作站一样普遍,而云计算正在使分布式内存系统像台式计算机一样普及。尽管如此,大多数计算机科学专业的学生在毕业时少有并行编程的经验。许多学院和大学提供并行计算方面的高年级选修课程,但由于大多数计算机科学专业的必修课程繁重,许多人在毕业时都没有编写过多线程或多进程程序。
似乎很明显,这种状况需要改变。虽然许多程序可以在单核处理器上获得令人满意的性能,但应该让计算机科学家意识到可以通过并行性获得潜在的巨大性能改进,并且他们应该能够在需要时利用这种潜力。
编写本书就是为了尝试解决这个问题。书中介绍了如何利用MPI、Pthreads、OpenMP和CUDA这四种广泛使用的并行编程API编写并行程序,目标读者是需要编写并行程序的学生和专业人士。阅读本书的先决条件很简单:学习过大学水平的数学课程和具备用C语言编写串行程序的能力。
先决条件是最基础的,因为我们认为学生应该尽早开始并行系统编程。在旧金山大学,计算机科学专业的学生在大一第一学期修读“计算机科学导论I”课程(这是大多数专业都会开设的课程)后,就可以立即基于本书学习并行编程课程,从而达到专业的要求。根据我们的经验,学生确实没有理由把学习编写并行程序推迟到大三或大四。相反,这门课程很受欢迎,学生发现在学习了这门课程之后,在其他课程中使用并行程序要容易得多。
如果大学二年级新生可以通过上课来学习编写并行程序,那么积极进取的计算机专业人士也应该能够通过自学来学习编写并行程序。我们希望这本书能成为他们的有用资源。
第2版重要更新
自本书第1版出版以来,已经过去了近十年。在这期间,并行编程的世界发生了很多变化,但令人惊讶的是,也有很多地方保持不变。我们编写第2版的目的是保留第1版中仍然普遍有用的材料,同时也在我们认为需要的地方添加新的材料。
最明显的补充是加入了关于CUDA编程的新的一章。第1版出版时,CUDA刚刚兴起。我们已经很清楚地看到,GPU在高性能计算中的应用将变得非常广泛,但在当时,GPGPU对于经验相对较少的程序员来说并不容易掌握。在过去的十年中,这种情况显然已经改变了。当然,CUDA不是一个标准,功能的增加、修改和删除都是非常迅速的。因此,使用CUDA的作者必须提出一个比标准(如MPI、Pthreads或OpenMP)变化更快的主题。尽管这样,我们希望本书对CUDA的介绍能在一段时间内持续有用。另一个很大的变化是Matthew Malensek作为合作者加入了我们。虽然Matthew是我们在旧金山大学的一位新同事,但他在并行计算的教学和应用方面都有丰富的经验。他在改善第2版的内容方面做出了很大的贡献。
关于本书
正如我们前面所指出的,本书的主要目的是向计算机科学背景有限、以前没有并行经验的读者讲授MPI、Pthreads、OpenMP和CUDA的并行编程。我们还想让这本书尽可能灵活,以便那些对学一两个API没有兴趣的读者仍然可以不费吹灰之力地阅读其余材料。因此,关于四个API的章节在很大程度上是相互独立的:可以按任何顺序阅读,其中一两章也可以省略。然而这种独立性也有一些代价:有必要重复这些章节中的一些材料。当然,重复的材料可以简单地浏览或跳过。
没有并行计算经验的读者应该先阅读第1章。该章试图提供相对非技术性的解释,说明为什么并行系统会在计算机领域占据主导地位。这一章还提供了关于并行系统和并行编程的简短介绍。
第2章介绍计算机硬件和软件的技术背景。第3章到第6章分别对MPI、Pthreads、OpenMP和CUDA进行介绍,且内容相对独立。第7章使用这四个API中的每一个开发两个不同的并行程序。最后,第8章提供了一些关于并行计算的补充信息。
我们使用C语言来开发程序,因为所有四个API都有C语言接口,而且,C语言是一种相对容易学习的语言—特别是对于C++和Java程序员来说,因为他们已经熟悉了C语言的控制结构。
教学建议
本书源于旧金山大学的一门低年级本科课程。该课程既满足计算机科学专业的要求,也满足学习本科操作系统、体系结构和网络课程的先决条件。该课程以为期四周的C语言编程学习开始。由于大多数学生已经编写过Java程序,这部分内容主要是关于C语言中指针的使用。课程的其余部分首先介绍MPI编程,然后是Pthreads和/或OpenMP,最后是关于CUDA的材料。
我们将大部分材料安排在第1、3、4、5和6章,一小部分材料安排在第2和7章。第2章的背景知识可根据需要学习。例如,在讨论OpenMP中的缓存一致性问题之前(第5章),我们先在第2章中介绍了关于缓存的材料。
课程作业包括每周的家庭作业、五次编程作业、几次期中考试和一次期末考试。家庭作业通常涉及编写一个非常短的程序或对现有程序进行小的修改。目的是确保学生与课程作业保持同步,并让学生亲身体验课堂上介绍的知识。似乎这些作业的存在是该课程成功的主要原因之一。本书中的大多数练习都适用于这些简短的作业。
编程作业比家庭作业复杂,但我们通常会给学生大量的指导:我们经常会在作业中加入伪代码,并在课堂上讨论比较困难的部分。这种额外的指导往往是至关重要的,以免学生在作业上花费过多时间。
期中考试和期末考试的结果以及讲授操作系统的教授充满热情的报告表明,该课程在教学生如何编写并行程序方面是非常成功的。
对于更高级的并行计算课程,本书及其在线支持材料可以作为补充。因此,关于四个API的语法和语义的大部分材料可以作为课外阅读材料。
本书也可以作为基于项目的课程和计算机科学以外的需要并行计算的课程的补充。
支持材料
本书的在线配套网站为www.elsevier.com/books-and-journals/book-companion/9780128046050。
这个网站包括勘误表和我们在本书中讨论的较长程序的完整源码。为教师提供的额外材料,包括可下载的图片和书中练习的答案,可以从https://educate.elsevier.com/9780128046050
下载。
我们将非常感谢读者让我们知道他们发现的任何错误。如果你确实发现了错误,请发送电子邮件至 [email protected]。
致谢
在本书的编写过程中,我们得到了许多人的帮助。我们要感谢第2版的审稿人Steven Frankel(以色列理工学院)和Il-Hyung Cho(萨吉诺谷州立大学),他们阅读并评论了CUDA章节的初稿。感谢那些阅读并评论了本书初稿的审稿人:Fikret Ercal(密苏里科技大学)、Dan Harvey(南俄勒冈大学)、Joel Hollingsworth(伊隆大学)、Jens Mache(路易斯和克拉克大学)、Don McLaughlin(西弗吉尼亚大学)、Manish Parashar(罗格斯大学)、Charlie Peck(厄勒姆学院)、Stephen C. Renk(北中央大学)、Rolfe Josef Sassenfeld(得克萨斯大学埃尔帕索分校)、Joseph Sloan(沃福德学院)、Michela Taufer(特拉华大学)、Pearl Wang(乔治梅森大学)、Bob Weems(得克萨斯大学阿灵顿分校)和Cheng-Zhong Xu(韦恩州立大学)。我们也深深感谢以下人士对本书各章的评论:Duncan Buell(南卡罗来纳大学)、Matthias Gobbert(马里兰大学巴尔的摩郡分校)、Krishna Kavi(北得克萨斯大学)、Hong Lin(休斯敦大学)、Kathy Liszka(阿克伦大学)、Leigh Little(纽约州立大学)、Xinlian Liu(胡德学院)、Henry Tufo(科罗拉多大学博尔得分校)、Andrew Sloss(ARM顾问工程师)和Gengbin Zheng(伊利诺伊大
学)。他们的意见和建议使本书内容得到了不可估量的改进。当然,我们对其余的错误和遗漏负全责。
幻灯片和第1版的答案手册分别由Kathy Liszka和Jinyoung Choi准备,感谢他们两位。
爱思唯尔公司的工作人员在整个项目中给予我们很大的帮助。Nate McFadden协助完成了文本的开发。Todd Green和Steve Merken是本书的组稿编辑。Meghan Andress是内容开发经理,Rukmani Krishnan是制作编辑,Victoria Pearson是设计师。他们的工作非常出色,我们对他们所有人都非常感激。
南加州大学计算机科学和数学系的同事在编写本书的过程中给予我们极大的帮助。Peter特别感谢Gregory Benson教授,他对并行计算的理解—特别是Pthreads和信号量—是一种宝贵的资源。非常感谢我们的系统管理员Alexey Fedosov和Elias Husary,他们耐心并有效地处理了我们在为本书编写程序时出现的所有“紧急事件”。他们还做了一项了不起的工作—为我们提供了用于进行所有程序开发和测试的硬件。
如果没有Holly Cohn、John Dean和Maria Grant的鼓励和精神支持,Peter将无法完成这本书,非常感谢他们的帮助。特别感谢Holly允许我们使用她的作品《七条记号》作为
封面。
Matthew要感谢南加州大学计算机科学系的同事,以及Maya Malensek和Doyel Sadhu,感谢他们的爱和支持。最重要的是感谢Peter Pacheco,作为在他学术生涯形成期的一位导师,Peter是建议和智慧的可靠来源。
我们最大的人情债来自我们的学生。他们一如既往地告诉我们什么是易于掌握的,什么是难以理解的。他们教会了我们如何讲授并行计算,对他们所有人表示最深切的感谢。
An Introduction to Parallel Programming, Second Edition
一段时间以来,并行硬件已经无处不在:很难找到不使用多核处理器的笔记本计算机、台式计算机或服务器。集群计算在今天几乎和20世纪90年代的高功率工作站一样普遍,而云计算正在使分布式内存系统像台式计算机一样普及。尽管如此,大多数计算机科学专业的学生在毕业时少有并行编程的经验。许多学院和大学提供并行计算方面的高年级选修课程,但由于大多数计算机科学专业的必修课程繁重,许多人在毕业时都没有编写过多线程或多进程程序。
似乎很明显,这种状况需要改变。虽然许多程序可以在单核处理器上获得令人满意的性能,但应该让计算机科学家意识到可以通过并行性获得潜在的巨大性能改进,并且他们应该能够在需要时利用这种潜力。
编写本书就是为了尝试解决这个问题。书中介绍了如何利用MPI、Pthreads、OpenMP和CUDA这四种广泛使用的并行编程API编写并行程序,目标读者是需要编写并行程序的学生和专业人士。阅读本书的先决条件很简单:学习过大学水平的数学课程和具备用C语言编写串行程序的能力。
先决条件是最基础的,因为我们认为学生应该尽早开始并行系统编程。在旧金山大学,计算机科学专业的学生在大一第一学期修读“计算机科学导论I”课程(这是大多数专业都会开设的课程)后,就可以立即基于本书学习并行编程课程,从而达到专业的要求。根据我们的经验,学生确实没有理由把学习编写并行程序推迟到大三或大四。相反,这门课程很受欢迎,学生发现在学习了这门课程之后,在其他课程中使用并行程序要容易得多。
如果大学二年级新生可以通过上课来学习编写并行程序,那么积极进取的计算机专业人士也应该能够通过自学来学习编写并行程序。我们希望这本书能成为他们的有用资源。
第2版重要更新
自本书第1版出版以来,已经过去了近十年。在这期间,并行编程的世界发生了很多变化,但令人惊讶的是,也有很多地方保持不变。我们编写第2版的目的是保留第1版中仍然普遍有用的材料,同时也在我们认为需要的地方添加新的材料。
最明显的补充是加入了关于CUDA编程的新的一章。第1版出版时,CUDA刚刚兴起。我们已经很清楚地看到,GPU在高性能计算中的应用将变得非常广泛,但在当时,GPGPU对于经验相对较少的程序员来说并不容易掌握。在过去的十年中,这种情况显然已经改变了。当然,CUDA不是一个标准,功能的增加、修改和删除都是非常迅速的。因此,使用CUDA的作者必须提出一个比标准(如MPI、Pthreads或OpenMP)变化更快的主题。尽管这样,我们希望本书对CUDA的介绍能在一段时间内持续有用。另一个很大的变化是Matthew Malensek作为合作者加入了我们。虽然Matthew是我们在旧金山大学的一位新同事,但他在并行计算的教学和应用方面都有丰富的经验。他在改善第2版的内容方面做出了很大的贡献。
关于本书
正如我们前面所指出的,本书的主要目的是向计算机科学背景有限、以前没有并行经验的读者讲授MPI、Pthreads、OpenMP和CUDA的并行编程。我们还想让这本书尽可能灵活,以便那些对学一两个API没有兴趣的读者仍然可以不费吹灰之力地阅读其余材料。因此,关于四个API的章节在很大程度上是相互独立的:可以按任何顺序阅读,其中一两章也可以省略。然而这种独立性也有一些代价:有必要重复这些章节中的一些材料。当然,重复的材料可以简单地浏览或跳过。
没有并行计算经验的读者应该先阅读第1章。该章试图提供相对非技术性的解释,说明为什么并行系统会在计算机领域占据主导地位。这一章还提供了关于并行系统和并行编程的简短介绍。
第2章介绍计算机硬件和软件的技术背景。第3章到第6章分别对MPI、Pthreads、OpenMP和CUDA进行介绍,且内容相对独立。第7章使用这四个API中的每一个开发两个不同的并行程序。最后,第8章提供了一些关于并行计算的补充信息。
我们使用C语言来开发程序,因为所有四个API都有C语言接口,而且,C语言是一种相对容易学习的语言—特别是对于C++和Java程序员来说,因为他们已经熟悉了C语言的控制结构。
教学建议
本书源于旧金山大学的一门低年级本科课程。该课程既满足计算机科学专业的要求,也满足学习本科操作系统、体系结构和网络课程的先决条件。该课程以为期四周的C语言编程学习开始。由于大多数学生已经编写过Java程序,这部分内容主要是关于C语言中指针的使用。课程的其余部分首先介绍MPI编程,然后是Pthreads和/或OpenMP,最后是关于CUDA的材料。
我们将大部分材料安排在第1、3、4、5和6章,一小部分材料安排在第2和7章。第2章的背景知识可根据需要学习。例如,在讨论OpenMP中的缓存一致性问题之前(第5章),我们先在第2章中介绍了关于缓存的材料。
课程作业包括每周的家庭作业、五次编程作业、几次期中考试和一次期末考试。家庭作业通常涉及编写一个非常短的程序或对现有程序进行小的修改。目的是确保学生与课程作业保持同步,并让学生亲身体验课堂上介绍的知识。似乎这些作业的存在是该课程成功的主要原因之一。本书中的大多数练习都适用于这些简短的作业。
编程作业比家庭作业复杂,但我们通常会给学生大量的指导:我们经常会在作业中加入伪代码,并在课堂上讨论比较困难的部分。这种额外的指导往往是至关重要的,以免学生在作业上花费过多时间。
期中考试和期末考试的结果以及讲授操作系统的教授充满热情的报告表明,该课程在教学生如何编写并行程序方面是非常成功的。
对于更高级的并行计算课程,本书及其在线支持材料可以作为补充。因此,关于四个API的语法和语义的大部分材料可以作为课外阅读材料。
本书也可以作为基于项目的课程和计算机科学以外的需要并行计算的课程的补充。
支持材料
本书的在线配套网站为www.elsevier.com/books-and-journals/book-companion/9780128046050。
这个网站包括勘误表和我们在本书中讨论的较长程序的完整源码。为教师提供的额外材料,包括可下载的图片和书中练习的答案,可以从https://educate.elsevier.com/9780128046050
下载。
我们将非常感谢读者让我们知道他们发现的任何错误。如果你确实发现了错误,请发送电子邮件至 [email protected]。
致谢
在本书的编写过程中,我们得到了许多人的帮助。我们要感谢第2版的审稿人Steven Frankel(以色列理工学院)和Il-Hyung Cho(萨吉诺谷州立大学),他们阅读并评论了CUDA章节的初稿。感谢那些阅读并评论了本书初稿的审稿人:Fikret Ercal(密苏里科技大学)、Dan Harvey(南俄勒冈大学)、Joel Hollingsworth(伊隆大学)、Jens Mache(路易斯和克拉克大学)、Don McLaughlin(西弗吉尼亚大学)、Manish Parashar(罗格斯大学)、Charlie Peck(厄勒姆学院)、Stephen C. Renk(北中央大学)、Rolfe Josef Sassenfeld(得克萨斯大学埃尔帕索分校)、Joseph Sloan(沃福德学院)、Michela Taufer(特拉华大学)、Pearl Wang(乔治梅森大学)、Bob Weems(得克萨斯大学阿灵顿分校)和Cheng-Zhong Xu(韦恩州立大学)。我们也深深感谢以下人士对本书各章的评论:Duncan Buell(南卡罗来纳大学)、Matthias Gobbert(马里兰大学巴尔的摩郡分校)、Krishna Kavi(北得克萨斯大学)、Hong Lin(休斯敦大学)、Kathy Liszka(阿克伦大学)、Leigh Little(纽约州立大学)、Xinlian Liu(胡德学院)、Henry Tufo(科罗拉多大学博尔得分校)、Andrew Sloss(ARM顾问工程师)和Gengbin Zheng(伊利诺伊大
学)。他们的意见和建议使本书内容得到了不可估量的改进。当然,我们对其余的错误和遗漏负全责。
幻灯片和第1版的答案手册分别由Kathy Liszka和Jinyoung Choi准备,感谢他们两位。
爱思唯尔公司的工作人员在整个项目中给予我们很大的帮助。Nate McFadden协助完成了文本的开发。Todd Green和Steve Merken是本书的组稿编辑。Meghan Andress是内容开发经理,Rukmani Krishnan是制作编辑,Victoria Pearson是设计师。他们的工作非常出色,我们对他们所有人都非常感激。
南加州大学计算机科学和数学系的同事在编写本书的过程中给予我们极大的帮助。Peter特别感谢Gregory Benson教授,他对并行计算的理解—特别是Pthreads和信号量—是一种宝贵的资源。非常感谢我们的系统管理员Alexey Fedosov和Elias Husary,他们耐心并有效地处理了我们在为本书编写程序时出现的所有“紧急事件”。他们还做了一项了不起的工作—为我们提供了用于进行所有程序开发和测试的硬件。
如果没有Holly Cohn、John Dean和Maria Grant的鼓励和精神支持,Peter将无法完成这本书,非常感谢他们的帮助。特别感谢Holly允许我们使用她的作品《七条记号》作为
封面。
Matthew要感谢南加州大学计算机科学系的同事,以及Maya Malensek和Doyel Sadhu,感谢他们的爱和支持。最重要的是感谢Peter Pacheco,作为在他学术生涯形成期的一位导师,Peter是建议和智慧的可靠来源。
我们最大的人情债来自我们的学生。他们一如既往地告诉我们什么是易于掌握的,什么是难以理解的。他们教会了我们如何讲授并行计算,对他们所有人表示最深切的感谢。
评论
还没有评论。