Snapdragon上的OpenCL介绍 (4)

46 篇文章 22 订阅

5 性能优化概述

本章提供OpenCL应用程序优化的高级概述。更详细的讨论可以在接下来的几章中找到。

注意:OpenCL应用程序的优化是具有挑战性的。它通常需要比最初的开发付出更多的努力。

5.1 性能可移植性

正如第2.4.2节所讨论的,OpenCL通常在不同架构之间并不具有良好的性能可移植性。即便OpenCL应用程序已经在其他平台上优化,并不代表着它在其它平台仍能够表现优良,特别是在离散图形处理器上,不太可能在Adreno图形处理器上表现良好。来自其他OpenCL供应商的编程指南和最佳实践可能根本不适用于Adreno gpu。因此,阅读整个文档对于优化Adreno gpu是非常重要的。此外,为一个Adreno GPU优化的OpenCL应用程序可能需要额外的调优或优化,以在其他Adreno GPU上实现最佳性能。

5.2 优化的高级视图

OpenCL应用程序的优化从上到下大致可以分为以下三个层次:

  • 应用程序/算法
  • API函数
  • 内核优化

OpenCL优化问题本质上是一个如何优化利用内存带宽和计算能力的问题,包括

  • 使用全局内存、本地内存、寄存器和缓存等的最佳方式。
  • 利用计算资源(如ALU和纹理操作)的最佳方法。

应用程序级优化策略将在本章的其余部分中讨论。其他级别将在下面的章节中介绍。

5.3 对OpenCL移植的初步评估

对于开发人员来说,在盲目地移植应用程序之前,评估应用程序是否适合OpenCL是很重要的。下面是GPU上OpenCL加速候选者的典型特征:

  • 大输入数据集

    • CPU和GPU之间的开销可能会远超过OpenCL在小输入数据集上的性能增益。
  • 计算密集型

    • GPUs有很多计算单元(ALUs),其峰值计算能力gflops通常比CPU高很多。为了充分利用GPU,应用程序应该具有相当高的计算复杂度。
  • 并行计算友好

    • 工作负载可以划分为独立的小单元,每个单元的处理不影响其他单元。
    • 并行任务需要充分利用GPU的内存延迟隐藏能力,这是使用GPU的一个关键好处。
  • 有限的分支控制流

    • GPU并不是像CPU那样有效地处理不同的控制流。如果用例需要很多条件检查和分支操作,CPU可能更合适。

5.4 移植CPU代码到OpanCL GPU

通常,开发人员可能已经有了一个用于OpenCL移植的基于CPU的参考程序。假设程序由许多小的功能模块组成。虽然在逐个映射的基础上将每个模块转换为OpenCL内核似乎很方便,但性能不太可能是最佳的。重要的是要考虑以下因素:

  • 在某些情况下,如果将多个CPU功能模块合并到一个OpenCL内核中,如果这样做可以减少GPU和内存之间的数据流量,那么可以带来更好的性能。
  • 在某些情况下,将一个复杂的CPU功能模块分解为多个更简单的OpenCL内核可以产生更好的单个内核并行化和更好的整体性能。
  • 开发人员可能需要修改数据结构,以一种可以减少总体数据流量的方式调整数据流。

5.5 GPU和CPU工作负载并行化

为了充分利用SOC的计算能力,当GPU执行内核时,应用程序可能会将某些任务委派给CPU。在设计这种拓扑和分配工作负载时,需要考虑以下几点:

  • 允许CPU运行最适合CPU的部分,如分散控制流和顺序操作。
  • 避免GPU空闲等待CPU完成的情况,反之亦然。
  • CPU和GPU之间的数据共享可能是昂贵的。相反,尝试将轻量级CPU任务转移到GPU,即使它可能对GPU不友好,以消除数据传输的需要。

5.6 瓶颈分析

识别和分析瓶颈是至关重要的,因为这将导致优化的重点领域。瓶颈会导致停滞,而且通常是应用程序中最慢的阶段。无论其他阶段的效率如何,应用程序的性能都受到其最慢阶段(即瓶颈)的限制。在瓶颈被解决之前,对区域的关注可能是没有意义的。

5.6.1 识别瓶颈

通常,内核要么是内存绑定,要么是计算绑定(也称为ALU绑定)。一个简单的技巧是操作内核代码并在设备上运行如下:

  • 如果增加更多的计算量不会改变性能,那么它可能不受计算限制。
  • 如果加载过多的数据不会改变性能,可能不是内存限制。

4.3小节中讨论的Snapdragon Profiler可以用来识别瓶颈。

5.6.2 解决瓶颈

一旦发现了瓶颈,就可以使用不同的策略来解决它:

  • 如果这是一个ALU绑定的问题,找到降低复杂性和计算数量的方法,例如在精度要求不高的地方使用快速放松数学或本机数学,并使用16位浮点格式而不是32位浮点格式。
  • 如果这是一个内存绑定问题,尝试改善内存访问,如向量化加载/存储,利用本地内存或纹理缓存(例如,使用只读的图像对象代替缓冲区对象)。使用更短的数据类型在GPU和全局内存之间加载/存储数据有利于节省内存流量。

详细信息将在下面的章节中进行描述。

注意:随着优化的进展,瓶颈可能会改变。如果内存瓶颈得到解决,那么内存限制问题就会变成ALU限制问题,反之亦然。为了获得最佳性能,需要进行多次来回迭代。

5.7 API级性能优化

OpenCL API函数在CPU主机上执行,用于管理资源和控制应用程序的执行。虽然从计算复杂性的角度来看,API函数通常比内核执行轻,但不恰当地使用API函数可能会导致很大的性能损失。这里有几点可以帮助开发人员避免一些常见的陷阱。

5.7.1合理安排API函数调用

开销较大的API函数应该被适当地放置,这样它们就不会阻塞或影响GPU工作负载的启动。一些OpenCL API函数需要很长时间才能执行,应该在执行循环之外调用。例如,下面的函数可能会花费很多时间来执行:

clCreateProgramWithSource()  
clBuildProgram() 
clLinkProgram() 
clUnloadPlatformCompiler() 

为了减少应用程序启动期间的执行时间,使用clCreateProgramWithBinary代替clCreateProgramWithSource。更多细节请参见5.7.3章节

注意:当clCreateProgramWithBinary失败时,不要忘记从源代码进行构建。如果OpenCL软件有不兼容的更新,这种情况可能会发生,但很少发生。

  • 避免在NDRange调用之间创建或释放内存对象。clCreate{Image|Buffer}的执行时间与请求的内存数量有关(如果使用了host_ptr)。
  • 如果可能,使用Android ION内存分配器。可以使用ION指针创建内存对象,而不是分配额外的内存并复制它。后续7.4章节将讨论如何使用ION memory。
  • 尝试在OpenCL中重用内存和上下文对象,以避免创建新的对象。总的来说,主机应该在GPU内核启动期间做一个轻量级的工作,以避免GPU的执行停滞。

5.7.2使用事件驱动的管道

OpenCL排队API函数可以接受一个事件列表,该列表指定了当前API函数开始执行之前必须完成的所有事件。同时,队列API函数还可以发出一个事件ID来标识自己。主机只需将API函数和内核提交给GPU执行,而无需担心它们的依赖性和完整性,如果依赖性在事件列表参数中被正确指定。通过使用这种方法,启动API函数调用的开销显著减少,因为软件可以以自己的最佳方式调度函数,并且主机不必在API函数调用之间进行干预。因此,非常希望使用事件驱动的管道来简化API函数。此外,开发者应该注意:

  • 避免阻塞API调用。阻塞调用会使CPU停止等待GPU完成,然后在下一次调用clEnqueueNDRangeKernel之前停止GPU。阻塞API调用主要用于调试。
  • 使用回调函数。从OpenCL 1.2开始,许多API函数被增强或修改,以接受用户定义的回调函数来处理事件,这种异步调用机制允许更有效的管道执行,因为宿主现在处理事件更灵活了。

5.7.3 内核加载和构建

在运行时加载和构建内核源代码可能代价昂贵。有些应用程序可能更喜欢实时生成源代码,因为某些参数可能无法预先使用。如果源代码的生成和编译不影响GPU的执行,这可能是好的。但一般来说,不鼓励动态源代码生成。

与实时构建源代码不同,更好的方法是离线构建源代码,并且只使用二进制内核。当应用程序被加载时,二进制内核代码也被加载。这样做将显著减少从磁盘加载代码的开销。

如果应用程序针对不同层次的Adreno设备,则需要不同版本的二进制代码。关于兼容性,需要注意以下几点:

  • 二进制代码只能用于编译它的特定GPU。如果一个二进制文件是在拥有Adreno A530 GPU的设备上构建的,那么它就不能在拥有Adreno A540 GPU的设备上使用。
  • 向后兼容性可跨编译器版本。如果目标GPU相同,编译器的新版本可能支持较旧的二进制文件。

如果发现不兼容的二进制内核,请使用clCreateProgramWithSource作为回退解决方案。

5.7.4使用顺序命令队列

Adreno OpenCL平台支持无序命令队列。但是,由于实现无序命令队列所需的依赖关系管理,因此会产生更大的开销。Adreno软件将命令输送到有序队列。因此,最好使用有序的命令队列而不是无序的命令队列。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值