首页>参考读物>计算机科学与技术>软件与程序设计

CUDA C编程权威指南
作者 : [美]程润伟(John Cheng) 马克斯?格罗斯曼(Max Grossman) 泰•麦克切尔(Ty McKercher) 著
译者 : 颜成钢 殷建 李亮 译
出版日期 : 2017-05-31
ISBN : 978-7-111-56547-5
定价 : 99.00元
扩展资源下载
扩展信息
语种 : 简体中文
页数 : 432
开本 : 16
原书名 : Professional CUDA C Programming
原出版社: John Wiley & Sons(USA)
属性分类: 店面
包含CD :
绝版 : 未绝版
图书简介

图书前言

欢迎来到用CUDA C进行异构并行编程的奇妙世界!
现代的异构系统正朝一个充满无限计算可能性的未来发展。异构计算正在不断被应用到新的计算领域—从科学到数据库,再到机器学习的方方面面。编程的未来将是异构并行编程的天下!
本书将引领你通过使用CUDA平台、CUDA工具包和CUDA C语言快速上手GPU(图形处理单元)计算。本书中设置的范例与练习也将带你快速了解CUDA的专业知识,助你早日达到专业水平!
本书写给谁
本书适用于任何想要利用GPU计算能力来提高应用效率的人。它涵盖了CUDA C编程领域最前沿的技术,并有着以下突出的优势:
风格简洁
描述透彻
大量范例
优质习题
覆盖面广
内容聚焦高性能计算的需求
如果你是一个经验丰富的C程序员,并且想要通过学习CUDA C来提高高性能计算的专业才能,本书中建立在你现有知识之上的例题和习题,将使掌握CUDA C编程更加简单。仅需掌握一些C语言延伸的CUDA知识,你便可以从大量的并行硬件中获益。CUDA平台、编程模型、工具和库将使得异构架构编程变得简捷且高效。
如果你是计算机科学领域以外的专业人士,而且想要通过GPU上的并行编程来最大限度地提高工作效率,并提高应用性能,那么本书正是为你量身打造的。书中的阐述清晰而简明,专人精心设计的示例,使用配置文件驱动的方法,这些都将帮助你深入了解GPU编程并迅速掌握CUDA。
如果你是教授或任何学科的研究者,希望通过GPU计算推进科学发现和创新,本书中将有你找到解决方案的捷径。即使你没有多少编程经验,在并行计算概念和计算机科学的知识方面也不够精通,本书也可带你快速入门异构架构并行编程。
如果你是C语言初学者并且有兴趣探索异构编程,本书也完全适合你,因为它不强制要求读者有丰富的C语言编程经验。即使CUDA C和C语言使用相同的语法,二者的抽象概念和底层硬件也是全然不同的,因而对其中之一的经验并不足以使你在学习另一个时感到轻松。所以,只要你对异构编程有浓厚的兴趣,只要你乐于学习新事物且乐于尝试全新的思维方式,只要你对技术相关的话题有深入探索的热情,本书也完全适合你。
即使你有不少关于CUDA C的经验,本书还是有助于知识更新、探索新工具以及了解最新CUDA功能。虽然本书旨在从零开始培养CUDA的专业人才,但它也含有许多先进的CUDA概念、工具和框架的概述,它们将对CUDA开发人员大有裨益。
本书的内容
本书讲解了CUDA C编程的基本概念与技术,用于大幅加速应用程序的性能,并包含了随着CUDA工具包6.0和NVIDIA Kepler GPU一起发布的最新功能。在对从同质架构到异构架构的并行编程模式转变进行了简要介绍之后,本书将引导你学习必要的CUDA编程技能和最佳的练习实践,包含但不仅限于CUDA编程模型、GPU执行模型、GPU内存模型、CUDA流和事件、多GPU编程的相关技术、CUDA感知MPI编程和NVIDIA开发工具。
本书采用一种独特的方法来教授CUDA知识,即将基础性的概念讲解与生动形象的示例相结合,这些示例使用配置文件驱动的方法来指导你实现最佳性能。我们对每一个主题都进行了详尽的讲解,清晰地展示出了采用代码示例形式详细操作的过程。书中不仅教授如何使用基于CUDA的工具,还介绍了如何以抽象编程模型为基础并凭借悟性与直觉对开发过程每一步骤的结果做出解释,从而帮助你快速掌握CUDA的开发流程。
每章围绕一个主题展开讲解,运用可行的代码示例来演示GPU编程的基本功能和技术,这之后就是我们精心设计的练习,以便你进一步探索加深理解。
所有的编程示例都是在装有CUDA 5.0(或更高版本)和Kepler或Fermi GPU的Linux系统上运行的。由于CUDA C是一种跨平台的语言,因而书中的示例在其他平台上也同样适用,比如嵌入式系统、平板电脑、笔记本电脑、个人电脑、工作站以及高性能计算服务器。许多OEM供应商支持各种类型的NVIDIA GPU。
本书的结构
本书共有10章,包含了以下主题。
第1章:基于CUDA的异构并行计算
本章首先简要介绍了使用GPU来完善CPU的异构架构,以及向异构并行编程进行的模式转变。
第2章:CUDA编程模型
本章介绍了CUDA编程模型和CUDA程序的通用架构,从逻辑视角解释了在CUDA中的大规模并行计算:通过编程模型直观展示的两层线程层次结构。同时也探讨了线程配置启发性方法和它们对性能的影响。
第3章:CUDA执行模型
本章通过研究成千上万的线程是如何在GPU中调度的,来探讨硬件层面的内核执行问题。解释了计算资源是如何在多粒度线程间分配的,也从硬件视角说明了它如何被用于指导内核设计,以及如何用配置文件驱动方法来开发和优化内核程序。另外,本章还结合示例阐述了CUDA的动态并行化和嵌套执行。
第4章:全局内存
本章介绍了CUDA内存模型,探讨全局内存数据布局,并分析了全局内存的访问模式。本章介绍了各种内存访问模式的性能表现,阐述了统一内存和CUDA 6.0中的新功能是如何简化CUDA编程的,以及如何提高程序员工作效率。
第5章:共享内存和常量内存
本章阐释了共享内存,即管理程序的低延迟缓存,是如何提高内核性能的。它描述了共享内存的优化数据布局,并说明了如何避免较差的性能。最后还说明了如何在相邻线程之间执行低延迟通信。
第6章:流和并发
本章介绍了如何使用CUDA流实现多内核并发执行,如何重叠通信和计算,以及不同的任务分配策略是如何影响内核间的并发的。
第7章:调整指令级原语
本章解释了浮点运算、标准的内部数学函数和CUDA原子操作的性质。它展示了如何使用相对低级别的CUDA原语和编译器标志来优化应用程序的性能、准确度和正确性。
第8章:GPU加速库和OpenACC
本章将介绍实现程序并行的CUDA专用函数库,包括线性代数、傅里叶变换和随机数生成等范例。本章还解释了OpenACC和基于编译器指令的GPU编程模型是如何利用更简单的方法辅助CUDA挖掘GPU计算能力的。
第9章:多GPU编程
本章介绍了支持P2P GPU内存访问的GPUDirect技术,阐述了如何在多个GPU上管理和执行计算问题,还说明了在GPU加速计算集群上的大规模应用是如何利用MPI与GPUDirect RDMA来实现性能线性扩展的。
第10章:程序实现的注意事项
本章介绍了CUDA的开发过程和各种配置文件驱动的优化策略,演示了如何使用CUDA调试工具来调试内核和内存错误,通过案例教你如何将一个传统的C程序一步步移植到CUDA C中,以有助于加强你对于这一方法的理解,同时将此过程可视化,并验证了这些工具。
阅读本书前的准备
本书不对GPU作特殊要求,使用本书前也不需要你具备并行编程的相关经验,不过你最好会一些Linux的基本操作。运行本书各示例代码的理想机器环境是:安装有CUDA 6.0工具包的Linux系统,C/C++ 6.0编译程序和NVIDIA Kepler GPU。
尽管某些使用CUDA 6.0的示例可能需要Kepler GPU,但大多数示例仍需在Fermi设备上运行。它们中的大多数可以使用CUDA 5.5来编译。
CUDA工具包的下载
你可以从https://developer.nvidia.com/cuda-toolkit下载CUDA 6.0工具包。
该CUDA工具包包括了NVIDIA GPU编译器、CUDA数学库以及用于调试和优化应用程序性能的工具,此外还有编程指南、用户手册、API参考指南和其他文档,它们都将帮助你快速掌握GPU应用程序的开发。
规范
为了使你的阅读体验达到最佳,我们在书中使用了一些规范。对于新出现的术语和重要的词语,在其第一次被引入时,我们会对它们进行突出强调。文中出现的文件名、URL地址和代码表示如下:

我们用以下方式表示代码:

我们按如下方式介绍CUDA运行时的函数:

我们按如下方式显示程序输出:

我们用以下方式给出命令行指令:

源代码
在学习本书中的示例时,你可以选择手动输入所有代码或者使用附在本书上的源代码。所有本书中的源代码都可在www.wrox.com/go/procudac下载。在页面上,只要找到本书的标题(通过搜索框查找或直接查看书名列表),然后单击书中的详细信息页面上的代码下载链接,就可以获取书中所有的源代码。
在完成每章结尾的练习时,推荐你利用参考示例代码亲自编写代码。所有练习用的代码文件同样也可从Wrox网站下载。
勘误表
尽管我们已竭尽所能来避免书中出现文本或代码错误,然而事实上,难免百密一疏。如果你在阅读本书的过程中发现了如拼写或编码上的错误,并愿意与我们联系进行反馈,我们将不胜感激。若你能帮助勘误,这将会使其他读者免于在困惑中长久徘徊,同时,你也是在帮助我们给读者提供更加高质量的信息。
本书的勘误表可在www.wrox.com/go/procudac处找到。在本书的详细信息页面,点击图书勘误表链接即可。在这个页面上,你可以查看所有已提交并由Wrox的编辑发布的勘误表。

上架指导

计算机\程序设计

作者简介

[美]程润伟(John Cheng) 马克斯?格罗斯曼(Max Grossman) 泰•麦克切尔(Ty McKercher) 著:暂无相关简介

译者简介

颜成钢 殷建 李亮 译:暂无简介

译者序

CUDA(Compute Unified Device Architecture,统一计算设备架构)是NVIDIA(英伟达)提出的并行计算架构,结合了CPU和GPU的优点,主要用来处理密集型及并行计算。CPU和GPU是两个独立的处理器,通过单个计算节点中的PCI-Express总线相连,GPU用来提高计算密集型应用程序中并行程序段的执行速度,CPU则负责管理设备端的资源。CUDA编程的独特优势在于开放的架构特性可以使程序员在功能强大的硬件平台上充分挖掘其并行性,既满足了计算密集型程序的需要,又实现了程序的易读性及便捷性。
对任何想要使用GPU来进行科研或技术编程的人来说,本书都将是一个珍贵的资源宝库,它全面介绍了CUDA编程接口及其用法,包含但不局限于CUDA编程模型、GPU执行模型、GPU内存模型、CUDA流和事件、多GPU编程的相关技术、CUDA库等内容。此外,本书列举了大量的范例来帮助读者入门,并提供了程序下载地址,供读者亲自运行体验。在基本掌握了本书的内容之后,你将发现CUDA C编程是一项十分简单、有趣且高效的工作。
本书第1章简要介绍了GPU和CPU构成的异构架构,对使用CUDA进行异构并行计算的原理进行了简要说明。第2章介绍了CUDA编程模型,从逻辑上解释了什么是CUDA大规模并行计算。第3章介绍了CUDA执行模型。第4章给出了CUDA内存模型,分析了全局内存的访问模式。第5章介绍了数据在共享内存中的存储方式,以及如何使用共享内存提高函数性能。第6章讲述了如何使用CUDA流实现网格级的并发。第7章介绍了计算密集型应用程序中的CUDA指令级原语。第8章介绍了CUDA函数库及其作用域,包括线性代数、傅里叶变换、随机数等。第9章讲述了多GPU编程。第10章则说明了在使用CUDA进行程序开发时的一些注意事项。
译者在翻译过程中力求忠于原著,但限于译者的能力及水平,如有纰漏,还望读者朋友们不吝赐教,译者在此表示衷心的感谢。

推荐序

图形处理器(GPU)已经有了很大的发展。从最初能够瞬时生成图像输出的专业图形处理器,发展到显示单元,GPU已经成为人们在进行超高速数据处理时热衷的技术手段。在过去的几年中,GPU已经越来越多地附加到CPU上,用于加速异构计算中的各种计算。如今,许多桌面系统、计算集群,甚至世界上许多最大的超级计算机都配置有GPU。图形处理器有着用于专业技术计算的大数据计算能力,它们已经在广泛的学科领域中实现了科学和工程的巨大进步,因为它们使大量计算核心并行工作,同时又将功耗预算保持在合理范围内。
所幸,GPU编程接口也一直与时俱进。过去,要在应用功能之外使用它们要费好大一番功夫,GPU编程人员不得不去熟练掌握那些通常只有图形程序员才能理解到位的诸多概念。如今的系统提供了一个更加便捷的手段来创建可以在其上运行的应用软件,这就是CUDA。
CUDA是最受欢迎的应用编程接口之一,可用于加速GPU上的一系列计算内核。它可以让C或C++代码在GPU上合理高效地运行。CUDA既满足了为使其良好运行而了解其架构的需要,又有为了使用和输出可读程序的编程接口,并且完美地平衡了这两项功能。
对任何想要使用GPU来进行科研或技术编程的人来说,该书都将是一个珍贵的资源宝库。它全面介绍了CUDA编程接口及其用法。首先,它描述了在异构架构上并行计算的基础知识,并介绍了CUDA的功能。其次,它解释了CUDA程序的执行方式。由于CUDA为程序员提供了操作手段和内存模型,因而CUDA程序员直接掌控了大规模并行环境。除了提供CUDA内存模型的细节之外,该书还提供了丰富的关于如何使用CUDA的信息。接下来的章节将讨论流,以及如何执行并行和重叠的内核。然后是关于调整指令级原语,关于如何使用CUDA库,以及关于使用OpenACC指令进行GPU编程的部分。在第9章之后,本书还提供了一些应用时的注意事项。此外,书中列举大量的范例来帮助读者更好地入门,读者可以下载并亲自运行体验。
实践证明,CUDA在表现力和可编程性之间实现了绝佳的平衡。但是,以简化应用开发为使命的人深知,路漫漫其修远兮。在过去的几年中,CUDA的研究人员一直在努力改善异构编程工具。CUDA 6.0引入了许多新功能,包括统一的内存和插件库,极大地简化了GPU编程。它们还提供了一组名为OpenACC的指令,这在书中也有所涉及。OpenACC有望进一步完善CUDA,它将减少在程序执行中所需的直接控制,与此同时提供一个更加便捷的方法来利用GPU的编程能力。目前看来,收效良好。无论是OpenACC、CUDA 6.0,还是书中讲到的其他主题,都将帮助CUDA开发人员加快应用程序的运行,从而实现前所未有的高性能。请相信我,这本书值得占据你书架的一席之地!
祝大家编程愉快!

芭芭拉·查普曼
休斯敦大学计算机辅助创新设计系统与计算机系

图书目录

译者序
推荐序
自序
作者简介
技术审校者简介
前言
致谢
第1章 基于CUDA的异构并行计算1
1.1 并行计算1
1.1.1 串行编程和并行编程2
1.1.2 并行性3
1.1.3 计算机架构4
1.2 异构计算6
1.2.1 异构架构7
1.2.2 异构计算范例9
1.2.3 CUDA:一种异构计算平台10
1.3 用GPU输出Hello World12
1.4 使用CUDA C编程难吗15
1.5 总结16
1.6 习题16
第2章 CUDA编程模型18
2.1 CUDA编程模型概述18
2.1.1 CUDA编程结构19
2.1.2 内存管理20
2.1.3 线程管理24
2.1.4 启动一个CUDA核函数29
2.1.5 编写核函数30
2.1.6 验证核函数31
2.1.7 处理错误32
2.1.8 编译和执行32
2.2 给核函数计时35
2.2.1 用CPU计时器计时35
2.2.2 用nvprof工具计时39
2.3 组织并行线程40
2.3.1 使用块和线程建立矩阵索引40
2.3.2 使用二维网格和二维块对矩阵求和44
2.3.3 使用一维网格和一维块对矩阵求和47
2.3.4 使用二维网格和一维块对矩阵求和48
2.4 设备管理50
2.4.1 使用运行时API查询GPU信息50
2.4.2 确定最优GPU53
2.4.3 使用nvidia-smi查询GPU信息53
2.4.4 在运行时设置设备54
2.5 总结54
2.6 习题55
第3章 CUDA执行模型56
3.1 CUDA执行模型概述56
3.1.1 GPU架构概述57
3.1.2 Fermi架构59
3.1.3 Kepler架构61
3.1.4 配置文件驱动优化65
3.2 理解线程束执行的本质67
3.2.1 线程束和线程块67
3.2.2 线程束分化69
3.2.3 资源分配74
3.2.4 延迟隐藏76
3.2.5 占用率78
3.2.6 同步81
3.2.7 可扩展性82
3.3 并行性的表现83
3.3.1 用nvprof检测活跃的线程束84
3.3.2 用nvprof检测内存操作85
3.3.3 增大并行性86
3.4 避免分支分化88
3.4.1 并行归约问题88
3.4.2 并行归约中的分化89
3.4.3 改善并行归约的分化93
3.4.4 交错配对的归约95
3.5 展开循环97
3.5.1 展开的归约97
3.5.2 展开线程的归约99
3.5.3 完全展开的归约101
3.5.4 模板函数的归约102
3.6 动态并行104
3.6.1 嵌套执行105
3.6.2 在GPU上嵌套Hello World106
3.6.3 嵌套归约109
3.7 总结113
3.8 习题113
第4章 全局内存115
4.1 CUDA内存模型概述115
4.1.1 内存层次结构的优点116
4.1.2 CUDA内存模型117
4.2 内存管理124
4.2.1 内存分配和释放124
4.2.2 内存传输125
4.2.3 固定内存127
4.2.4 零拷贝内存128
4.2.5 统一虚拟寻址133
4.2.6 统一内存寻址134
4.3 内存访问模式135
4.3.1 对齐与合并访问135
4.3.2 全局内存读取137
4.3.3 全局内存写入145
4.3.4 结构体数组与数组结构体147
4.3.5 性能调整151
4.4 核函数可达到的带宽154
4.4.1 内存带宽154
4.4.2 矩阵转置问题155
4.5 使用统一内存的矩阵加法167
4.6 总结171
4.7 习题172
第5章 共享内存和常量内存174
5.1 CUDA共享内存概述174
5.1.1 共享内存175
5.1.2 共享内存分配176
5.1.3 共享内存存储体和访问模式176
5.1.4 配置共享内存量181
5.1.5 同步183
5.2 共享内存的数据布局185
5.2.1 方形共享内存185
5.2.2 矩形共享内存193
5.3 减少全局内存访问199
5.3.1 使用共享内存的并行归约199
5.3.2 使用展开的并行归约202
5.3.3 使用动态共享内存的并行归约204
5.3.4 有效带宽205
5.4 合并的全局内存访问205
5.4.1 基准转置内核205
5.4.2 使用共享内存的矩阵转置207
5.4.3 使用填充共享内存的矩阵转置210
5.4.4 使用展开的矩阵转置211
5.4.5 增大并行性214
5.5 常量内存215
5.5.1 使用常量内存实现一维模板215
5.5.2 与只读缓存的比较217
5.6 线程束洗牌指令219
5.6.1 线程束洗牌指令的不同形式220
5.6.2 线程束内的共享数据222
5.6.3 使用线程束洗牌指令的并行归约226
5.7 总结227
5.8 习题228
第6章 流和并发230
6.1 流和事件概述231
6.1.1 CUDA流231
6.1.2 流调度234
6.1.3 流的优先级235
6.1.4 CUDA事件235
6.1.5 流同步237
6.2 并发内核执行240
6.2.1 非空流中的并发内核240
6.2.2 Fermi GPU上的虚假依赖关系242
6.2.3 使用OpenMP的调度操作244
6.2.4 用环境变量调整流行为245
6.2.5 GPU资源的并发限制246
6.2.6 默认流的阻塞行为247
6.2.7 创建流间依赖关系248
6.3 重叠内核执行和数据传输249
6.3.1 使用深度优先调度重叠249
6.3.2 使用广度优先调度重叠252
6.4 重叠GPU和CPU执行254
6.5 流回调255
6.6 总结256
6.7 习题257
第7章 调整指令级原语258
7.1 CUDA指令概述259
7.1.1 浮点指令259
7.1.2 内部函数和标准函数261
7.1.3 原子操作指令262
7.2 程序优化指令264
7.2.1 单精度与双精度的比较264
7.2.2 标准函数与内部函数的比较266
7.2.3 了解原子指令272
7.2.4 综合范例277
7.3 总结279
7.4 习题280
第8章 GPU加速库和OpenACC281
8.1 CUDA库概述282
8.1.1 CUDA库支持的作用域283
8.1.2 通用的CUDA库工作流283
8.2 cuSPARSE库285
8.2.1 cuSPARSE数据存储格式286
8.2.2 用cuSPARSE进行格式转换289
8.2.3 cuSPARSE功能示例289
8.2.4 cuSPARSE发展中的重要主题291
8.2.5 cuSPARSE小结291
8.3 cuBLAS库292
8.3.1 管理cuBLAS数据293
8.3.2 cuBLAS功能示例294
8.3.3 cuBLAS发展中的重要主题295
8.3.4 cuBLAS小结296
8.4 cuFFT库296
8.4.1 使用cuFFT API296
8.4.2 cuFFT功能示例298
8.4.3 cuFFT小结299
8.5 cuRAND库299
8.5.1 拟随机数或伪随机数的选择299
8.5.2 cuRAND库概述300
8.5.3 cuRAND介绍303
8.5.4 cuRAND发展中的重要主题306
8.6 CUDA 6.0中函数库的介绍307
8.6.1 Drop-In库307
8.6.2 多GPU库308
8.7 CUDA函数库的性能研究310
8.7.1 cuSPARSE与MKL的比较310
8.7.2 cuBLAS与MKL BLAS的比较311
8.7.3 cuFFT与FFTW及MKL的比较311
8.7.4 CUDA库性能小结312
8.8 OpenACC的使用312
8.8.1 OpenACC计算指令的使用315
8.8.2 OpenACC数据指令的使用321
8.8.3 OpenACC运行时API325
8.8.4 OpenACC和CUDA库的结合327
8.8.5 OpenACC小结328
8.9 总结329
8.10 习题329
第9章 多GPU编程331
9.1 从一个GPU到多GPU332
9.1.1 在多GPU上执行333
9.1.2 点对点通信334
9.1.3 多GPU间的同步335
9.2 多GPU间细分计算336
9.2.1 在多设备上分配内存336
9.2.2 单主机线程分配工作337
9.2.3 编译和执行337
9.3 多GPU上的点对点通信338
9.3.1 实现点对点访问338
9.3.2 点对点的内存复制339
9.3.3 统一虚拟寻址的点对点内存访问341
9.4 多GPU上的有限差分342
9.4.1 二维波动方程的模板计算342
9.4.2 多GPU程序的典型模式343
9.4.3 多GPU上的二维模板计算344
9.4.4 重叠计算与通信347
9.4.5 编译和执行348
9.5 跨GPU集群扩展应用程序350
9.5.1 CPU到CPU的数据传输351
9.5.2 使用传统MPI在GPU和GPU间传输数据353
9.5.3 使用CUDA-aware MPI进行GPU到GPU的数据传输356
9.5.4 使用CUDA-aware MPI进行节点内GPU到GPU的数据传输357
9.5.5 调整消息块大小358
9.5.6 使用GPUDirect RDMA技术进行GPU到GPU的数据传输359
9.6 总结361
9.7 习题362
第10章 程序实现的注意事项364
10.1 CUDA C的开发过程364
10.1.1 APOD开发周期365
10.1.2 优化因素367
10.1.3 CUDA代码编译370
10.1.4 CUDA错误处理373
10.2 配置文件驱动优化374
10.2.1 使用nvprof寻找优化因素375
10.2.2 使用nvvp指导优化379
10.2.3 NVIDIA工具扩展381
10.3 CUDA调试383
10.3.1 内核调试383
10.3.2 内存调试390
10.3.3 调试小结395
10.4 将C程序移植到CUDA C的案例研究396
10.4.1 评估crypt396
10.4.2 并行crypt397
10.4.3 优化crypt398
10.4.4 部署crypt404
10.4.5 移植crypt小结407
10.5 总结407
10.6 习题407
附录 推荐阅读409

教学资源推荐
作者: John Lewis Peter J. DePasquale;Joseph Chase;
作者: 邱李华 郭志强 曹青
作者: Calvin Lin;Lawrence Snyder
参考读物推荐
作者: (美)Danil Zburivsky,Sudheesh Narayanan 著
作者: David Geary Rob Gordon