OpenCL异构并行编程实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-05-29 13:04:25

点击下载

作者:(美)泰(Tay,R.)

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

OpenCL异构并行编程实战

OpenCL异构并行编程实战试读:

前言

欢迎阅读本书。本书并不是浅尝辄止式的入门书籍,而是由开发人员编写且面向开发人员的专业图书。有些读者可能会对本书感到熟悉,而另一些读者则会感到陌生。本书浓缩了我使用OpenCL的经验,但更重要的是对异构计算环境进行编程的心得。我希望与读者分享我所掌握的知识,并且决定采取按照主题分类问题的组织方式。我力求让这些主题保持简洁,但不得不承认,其中一些主题有点长。这样做的原因在于选择的问题多种多样,而向读者展现这些问题是因为本书中的章节描述如何将相关技术应用于当前或未来的工作。本书有望成为一份有用的参考手册,随时供你查阅。我无疑希望这些问题的解决方案可以像帮助我一样协助你。

本书从软件开发人员的角度进行编写,面向不仅希望知道如何以并行方式编程,还希望了解如何以并行方式思考的那些读者。依我看,后者比前者更为重要,但两者隔离都不能解决任何问题。本书通过代码加强读者对每个概念的理解,并通过介绍更多主题对这些概念进行扩展。

本书的组织形式可以帮助你熟悉OpenCL的核心概念,从而轻松进入OpenCL领域。然后,我们会深入讨论这些概念,具体方式是将新获得的知识应用于各个主题以及在工作中会遇到的一般性并行计算问题。

为最有效地利用本书,强烈建议读者是软件开发人员或嵌入式软件开发人员,并且有兴趣了解并行软件开发,但并不真正知道从何处及如何开始学习。理想情况下,你应该了解一些C或C++知识(可以选择C,因为它相对简单),并且熟悉使用跨平台的生成系统,如Linux环境中的CMake。CMake的优点是它允许为熟悉使用Microsoft Visual Studio、Apple XCode或其他一些集成开发环境的开发人员建立生成环境。必须承认,本书中的示例没有使用这些工具。本书内容

第1章通过介绍使用OpenCL的目的和动机来为后续内容做好铺垫。在相应主题中概述了核心概念,这些主题涉及设备的内在本质和它们之间的交互;此外也通过真正可正常运行的代码介绍这些概念。读者将了解相关上下文和设备,以及如何创建在这些设备上运行的代码。

第2章讨论OpenCL中的缓冲区对象以及划分数据的策略。随后,读者将学习工作项的定义以及如何利用OpenCL抽象化实现数据划分。

第3章解释OpenCL提供的两种常规数据类型,即标量和向量数据类型,介绍如何使用这些数据类型解决不同的问题,以及OpenCL如何抽象化处理器中的原生向量架构。该章也会展示如何通过OpenCL产生可编程的向量。

第4章讨论OpenCL为解决日常问题而提供的各种函数,例如几何、置换和三角函数。该章还介绍如何使用对应的向量化函数加快执行速度。

第5章给出典型OpenCL开发的生命周期。该章也讨论一些数据划分策略,这些策略依赖于对所讨论算法的认知程度。读者会在不经意间发现,并非所有算法或问题都需要相同的处理方式。

第6章将指导你使用索贝尔的方法构建边缘检测滤波器。同时还会介绍一些数学概念,包括一维和二维中的卷积理论以及相应的代码。最后,该章介绍如何在OpenCL中进行性能分析及其在本主题中的应用。

第7章通过研究矩阵乘法的并行化形式以及应用从串行到并行的转换来讨论矩阵乘法的并行化。然后,该章将通过讨论如何增加计算吞吐量和提升缓存利用率来优化矩阵乘法。

第8章讨论并行计算的环境以及用于解决该问题的传统方法,即通过充分的数学计算实现共轭梯度。一旦读者对共轭梯度有直观了解,该章将介绍稀疏矩阵的各种存储格式如何影响并行计算,然后具体讨论ELLPACK、ELLPACK-R、COO和CSR格式。

第9章介绍各种排序算法,其中重点讨论并行排序网络,也称为双调排序。就像在其他所有章节中所做的那样,该章完成相关主题的讨论,具体包括给出相应理论及其串行实现,通过转换实现并行化,然后开发最终的并行版本。

第10章介绍基于排序算法(如快速排序)的非比较形式的经典示例,在这些示例中,此类算法更适合GPU架构。该章也会介绍另一种核心并行化编程技术,称为归约,帮助读者直观了解归约如何有助于基数排序更好地执行。基数排序的相关主题也演示了多种内核编程,突出介绍了它们的优缺点。学习本书的具体要求

读者需要能够在Linux环境下轻松工作,因为本书中的示例是针对Ubuntu 12.1064位操作系统进行测试的。下面是相关要求:

·GNU GCC C/C++编译器版本4.6.1(最低版本要求)

·AMD、Intel和NVIDIA提供的OpenCL 1.2 SDK

·AMD APP SDK版本2.8,带有AMD Catalyst Linux显卡驱动程序版本13.4

·Intel OpenCL SDK 2012

·CMake版本2.8(最低版本要求)

·Clang版本3.1(最低版本要求)

·Microsoft Visual C++2010(如果你在Windows上工作)

·Boost Library版本1.53

·VexCL(由Denis Demidov提供)

·AMD提供的CodeXL Profiler(可选)

·保证每天8小时睡眠

·开放的思想和虚心的态度

·一杯特浓咖啡或其他提神的饮料本书读者对象

本书面向特定的软件开发人员,这些开发人员希望了解可以用他们新购买的CPU或GPU做些什么,他们购买这些硬件的目的并不是用来玩计算机游戏。话虽如此,本书并不打算介绍仅可运行在家庭工作站上的算法。本书非常适合于有C/C++实操经验的开发人员,以及希望学习如何使用OpenCL编写在异构计算环境中执行的并行程序的开发人员。示例代码下载

本书中示例的代码可以从http://www.packtpub.com/support上注册并下载。第1章使用OpenCL

本章将介绍如下主题:

·查询OpenCL平台

·查询平台上的OpenCL设备

·查询OpenCL设备扩展

·查询OpenCL上下文

·查询OpenCL程序

·建立OpenCL内核

·建立命令队列以及对OpenCL内核排队1.1 引言

我们首先回顾一下计算的发展历史,并从以下角度探究OpenCL的重要性所在:OpenCL旨在统一异构设备的软件编程模型。OpenCL的目标是为个人计算机、服务器和手持设备/嵌入式设备中现代处理器的跨平台、并行编程制定完全免费的标准。Khronos组织负责完成OpenCL标准的制定工作,许多知名公司也参与其中,如Intel、ARM、AMD、NVIDIA、QUALCOMM、Apple等。借助OpenCL,只需要编写一次软件,就可以顺利地在各种支持此标准的设备上执行软件。OpenCL在此方面类似于Java,其优势在于现在可以采用统一的方法在这些设备上进行软件开发,具体实现方式是:通过各种数据结构揭示硬件,这些数据结构借助可编程应用接口(Application Programmable Interface,API)与硬件交互。目前,OpenCL支持包括x86、ARM、PowerPC在内的各种CPU以及AMD、Intel和NVIDIA提供的各种GPU。

开发人员已深刻体会到开发跨平台兼容软件的重要性,因为这样他们就可以在任意舒适的平台上开发应用程序,更不用说这也提供了一种一致的模型,借助该模型,可以将想法在程序中明确表述出来,而程序可以在支持此标准的任何设备上执行。然而,跨平台兼容性也意味着异构环境的存在,长期以来,开发人员都不得不学习并努力克服在为异构设备(范围从执行模型到存储系统)编写软件时产生的问题。在这些异构设备上开发软件时经常引发的另一项任务是:人们还期望开发人员能够清晰表述和提取这些设备中的并行项。在OpenCL出现之前,人们建立了各种编程语言及其基本原理,希望借助它们来清晰表述其执行任务的设备上的并行项;这些语言包括Fortran、OpenMP、MPI、VHDL、Verilog、Cilk、Intel TBB、Unified parallel C、Java等。虽然开发人员可能认为有必要掌握这些工具,因为这样可以给他们的简历添砖加瓦,但这些工具都是为同构环境而设计的。让我们回过头来看一下,上述情况表明了并没有一种统一的方法来清晰表述异构环境中的并行项。开发人员需要借助这些工具来提高工作效率,但通常会涉及并行分解,因为该过程在很大程度上与硬件相关,从而增加了工作时间。更为严重的是,许多开发人员过去只需要在同构计算环境中处理任务,但在过去几年中,对异构计算环境的需求正在不断增长。

人们对异构设备需求的不断增长在一定程度上是因为需要性能更高、反应更灵敏的系统,并且随着虚拟现实技术的逐渐流行,提升性能的一种可行方法是增加专门的处理单元来提取异构设备中的每个并行项,因为这是达到所需功率效率的唯一方式。人们向混合运算转变的主要推动力可以追溯到Anantha P.Chandrakasan进行的一项研究,名为“使用变换优化功率”(Optimizing power using Transformations)。该研究得出如下结论:从根本上说,多核芯片(其运行频率稍微低于同时代的CPU)实际上更加节能。未采用统一开发方法(例如OpenCL)进行异构计算的问题在于,开发人员需要掌握多种类型的ISA,并且存在多种并行级别及其存储系统。此处有必要提及NVIDIA开发的GPGPU计算工具包CUDA,这不仅是因为它在很大程度上类似于OpenCL,还因为它在学术界和业内被广泛采用。遗憾的是,CUDA只能驱动NVIDIA的GPU。

从异构环境中提取并行项的能力非常重要,这仅仅是因为计算应该是并行的,否则它就会违背OpenCL的整个初衷。幸运的是,主流的处理器公司都是Khronos组织领导的联盟体的一员,它们通过相应的机构主动实现了此标准。故事并未就此结束,不过好消息是开发人员意识到需要理解并行项及其在同构和异构环境中的工作方式。OpenCL的设计目标就是清晰地表述异构环境中的并行项。

长时间以来,开发人员基本上忽视了这一事实:他们的软件需要利用身边的多核机器的性能。因此,他们继续在单线程环境中开发软件,但情况已经发生变化(前面讨论了这种变化)。在多核领域中,开发人员需要掌握并发性的概念。并发性的优点是:通过合理地在线程之间分配资源,它可以最大化资源利用率。

使用多个处理单元并发执行软件,从而多个线程可以同时运行,这就是并行计算。开发人员面临的挑战是找出可以同时运行的线程并实现这种并发性。在OpenCL中,我们主要关注两个并行编程模型:任务并行和数据并行。

任务并行意味着开发人员可以创建并操作多个并发的任务。开发人员开发OpenCL的解决方案时,他们需要将问题分解为不同的任务,其中一些任务可以并发运行,这些任务会对应到并行环境的不同处理单元(PE),由其执行。另一方面,一些任务不能并发运行,甚至可能彼此独立。在任务之间共享数据的状况也带来了额外的复杂性。

在尝试实现数据并行时,开发人员需要重新调整考虑数据的方式以及如何并发读取和更新数据。并行计算中的一个常见问题是计算任意值数组中所有元素的和,同时存储中间的合计值。图1-1给出了一种可能的问题解决方法,其中应用了运算符,即任意二元结合运算符⊕。从概念上来说,开发人员可以使用任务来执行所输入的两个元素的加法,从而推导合计值。图 1-1

开发人员根据具体的问题来选择使用任务并行或数据并行,例如在遍历图表时应选择使用任务并行。无论开发人员更加倾向于使用何种模型,在通过OpenCL将程序对应到具体的硬件时,他们都需要面对相应的一组问题。在OpenCL面世之前,开发人员需要开发将在所需设备和通信器材上执行的模块,并且该模块需要能够与驱动程序进行I/O操作。此模块的一个示例是图形渲染程序,其中CPU初始化数据并建立所有对象,然后将渲染操作交给GPU完成。OpenCL旨在利用检测到的所有设备,从而实现资源利用最大化,在此方面它不同于“传统的”软件开发方式。

既然我们已很好地理解OpenCL,接下来就应该花费一些时间了解开发人员如何掌握OpenCL。不用为此感到担心,因为对于你所从事的每个项目,OpenCL只需要你理解如下方面:

·掌握所开发的异构系统的组成。

·通过探查了解这些设备的属性。

·使用任务并行、数据并行或结合使用两者开始并行程序分解,具体方法是将任务并行和数据并行表达为将在平台上运行的指令(也称为内核)。

·建立用于计算的数据结构。

·操作用于计算的内存对象。

·按照适当设备上所需的顺序执行内核。

·核对结果并验证其正确性。

接下来,我们需要深入查看OpenCL的各个组成部分来巩固上述要点。下面的组成部分共同构成了OpenCL架构。

·平台模型:平台实际上是一台主机,该主机连接到一个或多个OpenCL设备。每个设备可能包含多个计算单元(Compute Unit,CU),这些计算单元又可以分解为一个或(可能)多个处理单元,在这些处理单元上运行计算。

·执行模型:OpenCL程序执行时,会在主机上执行主机程序,主机程序发送内核以在平台上的一个或多个OpenCL设备中执行。

当提交内核以执行时,会定义一个索引空间,从而实例化工作项以执行该空间中的每个点。工作项由其全局ID标识,执行内核中表述的相同代码。工作项分为多个工作组,每个工作组有一个ID,通常称为工作组ID。一个工作组中的所有工作项在单个CU的PE上并发执行。

此处提及的索引空间称为NDRange,它描述一个N维空间,其中N的范围为1~3。每个工作项有一个全局ID,在分到工作组中时还会有一个本地ID,本地ID用于与组中的其他工作项进行区分,它派生于NDRange。工作组ID也具有相同的情况。接下来使用一个简单的示例来说明这些ID的工作方式。

给定两个数组A和B,每个数组有1024个元素。我们想要对这两个数组执行向量乘法(也称为点积)的计算,其中A的每个元素将乘以B中对应的元素。内核代码如下所示:

在此场景中,假设有1024个处理元素,我们会分配一个工作项来精确执行一个乘法操作,并且此处的工作组ID为0(因为只有一组),工作项ID的范围是{0…1023}。回顾一下前面的讨论,一个工作组的所有工作项可以在PE上并发执行。根据此讨论可以得出如下推断:这并不是利用设备的良好方式。

还是在此场景中,现在丢弃前面的假设,给出新的假设:仍然有1024个元素,但是将每4个工作项分到一组中,因此有256个工作组,这些工作组的ID范围是{0…255}。但需要注意的是,工作项的全局ID范围仍然是{0…1023},这是因为并没有增加待处理元素的数量。借助这种将工作项分到各自工作组中的方式,可以在这些设备中实现伸缩性,因为此方式通过确保所有PE都有可操作的工作项来提升执行效率。

NDRange可以在概念上映射到N维网格,图1-2说明了2DRange的工作方式,其中WG-X表示特定工作组的行长度,而WG-Y表示工作组的列长度;此外,该图还显示如何将工作项(包括它们各自的ID)分到工作组中。图 1-2

内核在设备上执行之前,主机程序扮演重要的角色,它会通过底层设备建立上下文,并且制定任务执行的顺序。主机程序通过确立如下对象的存在性(如果需要,则进行创建)来建立上下文:

·主机程序将要使用的所有设备

·OpenCL内核,即要在这些设备上运行的函数及其抽象

·内存对象,用于封装将由OpenCL内核使用/共享的数据

建立上下文之后,主机需要创建名为命令队列的数据结构。主机使用该数据结构协调设备上内核的执行,将命令发布到此队列并在设备上调度执行。命令队列可以接受如下命令:内核执行命令、内存传输命令和同步命令。此外,命令队列可以按照提供的顺序执行命令,也可以不按照顺序执行命令。如果将问题分解为多个独立的任务,则可以创建多个命令队列,这些命令队列分别针对不同的设备,并且在这些设备上调度任务。然后OpenCL将并发运行这些命令队列。

·内存模型:到目前为止,我们已经了解执行模型,接下来介绍OpenCL规定的内存模型。回顾一下,当内核执行时,它实际上是执行内核代码实例的工作项。因此,工作项需要从内存中读写数据,并且每个工作项都有权访问4种类型的内存:全局、常量、局部和私有。这些内存具有不同的大小和可访问性,其中全局内存具有最大的尺寸,并且工作项对其具有最广泛的可访问性;而私有内存在可访问性方面具有最大的限制,因为其对工作项来说是私有的。常量内存是存储不可变对象的只读内存,可由所有工作项共享。局部内存只可用于在工作组中执行的所有工作项,由每个计算单元持有,即CU特有的内存。

运行在主机上的应用程序使用OpenCL API在全局内存中创建内存对象,并将内存命令放入命令队列,排队执行这些命令。主机负责确保在内核开始执行时设备可以使用这些数据,具体方法是复制数据或映射/取消映射内存对象区域。在将数据从主机内存传输到设备内存期间,发出OpenCL命令以排队可能阻塞或非阻塞的内存传输操作。阻塞的内存传输和非阻塞的内存传输之间的区别是:对于前者,函数调用仅在认为安全时才返回(排队之后);对于后者,只要对命令排队,函数调用就会返回。

OpenCL中的内存映射可以将内存空间区域用于计算,此区域可以阻塞或非阻塞,并且开发人员可以将此区域视为可读、可写或可读写。

接下来将关注OpenCL的基础:实际开发一个小型的OpenCL程序,从而以编程方式帮助你深入了解如何使用OpenCL的平台和执行模型。

OpenCL规范1.2版是一个开放的、完全免费的标准,适用于在各种设备之间通过API进行的通用编程,这些设备包括移动CPU、传统CPU,后来又包括GPU。在编写本书时,该标准支持:

·基于数据和任务的并行编程模型。

·通过对并行性的扩展实现ISO C99的子集,但是带有一些限制,例如不支持递归、可变参数函数和宏。

·数学操作遵从IEEE 754规范。

·可以通过建立配置概要文件来移植到手持设备和嵌入式设备。

·与OpenGL、OpenGL ES和其他图形API互操作。

本书通篇将介绍如何精通OpenCL编程。

在阅读本书的过程中,你不仅可以了解如何使用API在OpenCL设备上执行各种操作,还可以掌握如何对问题建模并将其从串行程序转变为并行程序。你在此将要掌握的技术通常都可以应用于其他编程工具集。TMTMTM

我们使用过的工具集包括OpenCL、CUDA、OpenMP、TMTMTMMPI、Intel线程构建模块TM、Cilk、CilkPlus,它们使开发人员可以清晰表述同构环境中的并行项,找出学习工具以应用知识的整个过程,该过程将分为4个部分。这4个阶段相当常见,在使用过程中非常容易就能记住它们。希望你也能从中获益。

·查找并发性:程序员处理问题域以识别可用的并发性,揭示此并发性,将其用于算法设计中。

·算法结构:程序员使用高级结构组织并行算法。

·支持结构:此结构指的是如何组织并行程序以及如何使用相应技术管理共享的数据。

·实施机制:最后一步是查看特定的软件构造以实施并行编程。

不要担心不能理解这些概念,在学习本书的过程中,你将逐步了解它们。

接下来的一些主题将介绍该架构的平台模型,从而帮助你理解OpenCL API的使用方式。1.2 查询OpenCL平台

在开始编码工作之前,请确保你已为开发平台安装适当的OpenCL开发工具包。本节将演示如何使用OpenCL查询其平台,获取关于如下方面的简单信息:OpenCL检测到的兼容设备及其各种属性。1.2.1 准备工作

在我们的第一个OpenCL应用程序中,你将开始在计算机中查询所安装的OpenCL平台类型。在计算机设置中,你可能已经具有安装NVIDIA和AMD图形卡的配置,并且在此情况中你可能也安装了AMD APP SDK和NVIDIA的OpenCL工具包。因此,你已经安装所需的两种平台。

下面的代码清单从Ch1/platform_details/platform_details.c提取而来。1.2.2 实际工作

注意代码中包括的注释,它们将帮助你理解每个函数:

为了在UNIX平台上编译此代码,应运行类似于如下的编译命令:

运行此命令后,你将获得名为platform_details的二进制可执行文件。

要运行此程序,只须执行platform_details程序,示例输出将是OSX:1.2.3 工作说明

最初学习OpenCL编程时,它可能是一项使人畏缩的任务,但随着学习的深入,此项编程任务将会变得容易。因此,我们首先解释刚才给出的源代码。这是一个C源文件,可以注意到对其进行了排列,使得系统头文件总是位于顶部附近:

接下来是C程序员所谓的平台相关代码:

程序需要OpenCL头文件才能通过编译,因为这些头文件包含方法签名。现在我们将尝试了解其他代码所完成的工作。OpenCL中采用的编码规范之一是对数据类型添加前缀cl_,因此每个平台、设备和上下文的数据类型分别是cl_platform_XX、cl_device_XX、cl_context_XX;API也以类似的方式添加前缀cl,一种这样的API是clGetPlatformInfo。

在OpenCL中,API不会假设你确切地知道存在多少资源(例如平台、设备和上下文)或者在编写OpenCL代码时需要多少资源。为了编写可移植的代码,该语言的开发人员采用了一种聪明的方式来呈现API:用户可以使用同一个API提出一个一般性的问题,并且通过此API,基于问题的结果请求更多的信息。接下来通过一个示例说明这一点。

在代码中,可以注意到调用了clGetPlatformInfo()两次。第一次调用是查询在此机器上安装的平台数量。基于此查询的结果,再次调用clGetPlatformInfo(),但是这次传入上下文相关的信息,例如获得厂商的名称。在使用OpenCL编程时这种模式会重复出现,我认为其缺点是它偶尔会使API显得相当神秘,但其也有优点:它避免了在该语言中过多地使用API。

随后章节将讨论如何在OpenCL中将顺序代码转换为并行代码,不可否认的是,在涉及OpenCL编程的整个体系时,这一主题显得相当琐碎,但仍然有必要介绍。

让我们构建代码并查询OpenCL以获取连接到平台的设备。1.3 查询平台上的OpenCL设备

接下来查询安装在平台上的OpenCL设备。1.3.1 准备工作

1.3.2节讨论的代码清单给出了Ch1/device_details/device_details.c中的部分简略代码。此段代码演示了如何通过clGetDeviceIDs获得安装在平台上的设备的类型。你将使用此信息,通过将其传递给clGetDeviceInfo来检索关于此设备的详细数据。1.3.2 实际工作

对于此主题,你需要完整地引用适当的章节代码。注意其中包含的注释,因为它们将帮助你理解每个函数。我们已通过突出显示的说明文字包括此主题的主要部分:

在UNIX平台上,可以通过在终端上运行如下命令来编译device_details.c:

此外,应该在你的机器上本地存放名为device_details的二进制可执行文件。

执行此二进制可执行文件时,根据机器的硬件配置,你会看到不同的结果。对于笔者的OSX平台,下面是在具有Intel Core i5处理器、NVIDIA移动GPU GT330m的机器上执行此文件的输出(突出显示了扩展):

目前不需要过多关心此信息看起来是否有意义,随后章节将介绍此处信息的具体含义。1.3.3 工作说明

利用在前面小节中所做的工作,我们已经通过clGetPlatformInfo使用该平台,对其进行检测以查询连接的设备。在此使用了新的API函数clGetDeviceIDs和clGetDeviceInfo。首先使用clGetDeviceIDs尝试揭示连接到平台的设备的所有相关基本信息,然后使用clGetDeviceInfo遍历结果以了解关于设备功能的更多信息。在精心设计算法且不确定在何种设备上运行此算法时,这些信息就非常有价值。考虑到OpenCL支持各种处理器,这是一种编写可移植代码的优秀方式。

实际上还可以从设备获取更多的信息,强烈建议你参考http://www.khronos.org/registry/cl/sdk/2.0/docs/man/xhtml/,并查看clGetDeviceInfo的主页。

现在我们已了解如何查询平台和连接的设备,接下来应该查看如何查询OpenCL扩展。厂商可以利用这些扩展定义由OpenCL兼容设备提供的额外功能,而程序员则可以利用这些额外的功能进行开发。1.4 查询OpenCL设备扩展

OpenCL中的扩展让程序员可以利用设备厂商提供的额外功能,因此这些扩展是可选项。然而,一些扩展已被OpenCL认可,并且据称已获得主流厂商的支持。

下面是在OpenCL 1.2中已获得批准和支持的扩展的部分列表。如果想找出OpenCL公开的扩展和适配器(表1-1给出了部分扩展)的完整列表,请通过如下链接查阅PDF文档:http://www.khronos.org/registry/cl/specs/opencl-1.2-extensions.pdf。表 1-1

接下来利用前面已完成的代码,揭示如何确定哪些扩展在你的平台上获得支持和可用。1.4.1 准备工作

下面的代码清单仅显示Ch1/device_extensions/device_extensions.c中值得注意的部分代码。与OpenCL兼容的各种设备具有不同的功能,在开发应用程序期间,你无疑要确保某些扩展在使用之前就已存在。1.4.2节讨论的代码介绍如何检索这些扩展。1.4.2 实际工作

我们已包括主要的查询函数,该函数用于实现此特定的查询:

要编译此代码,与前面一样在你的终端上运行类似的命令,如下所示:

对于UNIX平台,下面是在具有Intel Core i5处理器、NVIDIA移动GPU GT330m的机器上执行此代码时获得的输出(突出显示了扩展):1.4.3 工作说明

研究我们所完成的工作,可以看到在此仅是利用已有的代码,并在需要的位置添加所需的功能,即添加代码来处理传入CL_DEVICE_EXTENSIONS的情况。我们在栈上建立了一个固定大小的数组,将该数组传递给clGetDeviceInfo,其中API最终将信息存入该数组。提取该信息只需要输出数组即可。作为一种高级用法,可以将该信息存入全局表结构,从而应用程序的其他部分可以使用它。

为理解这些扩展的含义以及利用它们的方式,建议你参考Khronos的OpenCL注册信息:http://www.khronos.org/registry/cl/。

我们不会过于深入地详述到目前为止介绍过的每个扩展。接下来了解OpenCL上下文。1.5 查询OpenCL上下文

通过一个或多个设备建立OpenCL上下文。OpenCL运行时使用上下文管理对象,例如命令队列(用于发送命令给设备的对象)、内存、程序和内核对象,并且用于在此上下文中指定的一个或多个设备上执行内核。

具体来说,可以通过如下方式创建OpenCL上下文:通过clCreateContext关联可用于平台的设备集合,或者通过clCreateContextFromType将上下文与特定类型的设备关联,例如CPU、GPU等。然而,无论通过何种方式,你都无法建立与多个平台关联的上下文。接下来使用1.1节中的向量乘法示例演示这些概念。可以使用如下方法解决向量乘法或点积的问题:笔和纸、CPU、GPU或GPU+CPU。显然,第一种方法在面对20个以上的元素时就显得无能为力,而使用OpenCL则可以获得更多的选择。需要决定的第一件事情是应该运行哪个平台,在OpenCL中这意味着决定是否使用AMD、NVIDIA、Intel等。接下来需要决定是在为此平台列出的所有设备上运行点积,还是在部分设备上运行。

因此,假设平台报告一个Inter Core i7 CPU和3个AMD GPU,且开发人员可以使用clCreateContextFromType限制针对CPU或GPU执行,但在使用clCreateContext时,理论上说,可以列出所有4个设备以针对其执行(然而,实际上很难有效地使用所有CPU和GPU,因为GPU可以比CPU推送更多的线程执行)。图1-3说明了开发人员可用于建立上下文的选项,假设主机环境同时安装有Intel和AMD的OpenCL平台软件。如果考虑Ivy Bridge Intel处理器,此配置会更加有趣,因为该处理器包括HD Graphics协处理器,从而让上下文可以同时知悉CPU和GPU。图 1-3

上下文有一个有趣的属性,即它保留一个引用计数,从而第三方库可以引用该上下文并因此利用设备。例如,如果在你的设备上可以使用cl_khr_d3d10_sharing扩展,则实际上可以在OpenCL和Direct3D 10之间交互操作,并将类似于内存对象的Direct3D 10资源作为可以读写的OpenCL内存对象进行处理。然而,本书不会演示此扩展的这一功能,而是留给读者进一步探究。1.5.1 准备工作

1.5.2节给出的代码清单提取自Ch1/context_query/context_details.c,说明了如何建立和发布OpenCL上下文。1.5.2 实际工作

为查询OpenCL上下文,需要在代码中包括类似于如下的函数。在学习本主题的过程中应当参考完整的代码清单:

在UNIX平台上,可以通过输入如下命令来编译并生成此程序:

在测试机器上有两个OpenCL兼容设备。第一个设备是Intel Core i5 CPU,第二个设备则是NVIDIA移动GT330m GPU。下面是相应的输出:1.5.3 工作说明

如果你一直按照本书的介绍顺序学习,那么应该了解到我们只是利用前面的练习,找出安装平台的类型,借助该类型揭示设备,并最终使用该信息建立相关的上下文。最后,我们可以查询获得的这些相关上下文。你可以注意到,上下文的引用计数在两种情况下都为1,这表明内存对象当前正在引用该上下文,并且我们传入CL_CONTEXT_REFERENCE_COUNT来反映这一点。只有在想要检测应用程序是否正在经历上下文泄漏(这实际上意味着一次内存泄漏)时,该计数器才会发挥作用。对于CPU和GPU等OpenCL设备,上下文泄漏听起来并不是大问题。但是对于移动处理器,这会造成非常严重的问题,因为一般来说,内存泄漏会浪费资源,并且最终极大地降低电池寿命。

在通过传入各种cl_context_info类型来使用clGetContextInfo查询上下文方面,实际上还有更多的详细信息。表1-2列出了这些详细信息。表 1-2

现在我们已经理解查询平台、设备、扩展和上下文的基础知识,接下来可以查看OpenCL内核以及如何对其进行编程。1.6 查询OpenCL程序

在OpenCL中,内核指的是在程序中声明的函数。OpenCL中的程序由一组内核组成,这些内核是在代码中使用_kernel限定符声明的函数。这样的程序封装上下文、程序源文件或二进制文件以及连接的内核数量。下面的小节解释如何生成OpenCL程序并最终在设备上加载内核以执行。1.6.1 准备工作

为了运行OpenCL内核,需要有一个程序(源文件或二进制文件)。目前可以通过两种方式生成程序:分别通过clCreateProgramWithSource和clCreateProgramWithBinary(这是清晰易懂的API名称),从源文件和二进制对象生成程序。如果成功执行,这两个API会返回由OpenCL类型cl_program表示的程序对象。接下来研究方法签名以更好地理解它:

如果仔细阅读该方法签名,你会注意到在从源文件生成程序之前需要创建OpenCL上下文。接下来,strings和lengths参数存放各种(内核)文件名和其对应的文件长度,最后一个参数errcode_ret反映在生成程序时是否存在错误:

研究该方法签名,可以快速了解到binaries和lengths参数存放指向程序二进制文件及其对应长度的指针。所有二进制文件通过上下文载入由device_list参数表示的设备。binary_status参数反映程序是否成功载入设备。如果二进制文件是唯一可以揭示给客户的人工对象,那么开发人员会发现这种程序创建方法会非常有用;在系统整合测试期间,该方法也非常有用。

为了能够使用clCreateProgramWithBinary,通过提取离线二进制文件来创建有效的OpenCL程序,开发人员需要首先使用平台的编译器生成离线二进制文件,然而此过程随厂商而不同。如果使用AMD APP SDK,则需要启用AMD扩展cl_amd_offline_devices,并且在创建上下文时,需要传入CL_CONTEXT_OFFLINE_DEVICES_AMD属性。如果正在为Intel或Apple OpenCL平台开发,则推荐你在这些公司的网站中查阅相关文档。

接下来,需要生成程序,方法是调用clBuildProgram并传入通过clCreateProgram FromSource创建的cl_program对象。在创建程序期间,开发人员可以为程序提供额外的编译器选项(如同在编译C/C++程序时执行的操作一样)。让我们查看一个示例,通过在1.6.2节中提供的代码了解如何完成该操作,这段代码是Ch1/build_opencl_program/build_opencl_program.c的简略版本,并且OpenCL内核文件列在Ch1/build_opencl_program/{simple.cl,simple_2.cl中。1.6.2 实际工作

为了查询OpenCl程序,需要在代码中包括类似于如下的函数。在学习本主题的过程中应当参考完整的代码清单:

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载