对于许多kernels来说,工作组大小的调整会是一种简单有效的方法。这章将会介绍基于工作组大小的基础知识,比如如何获取工作组大小,为什么工作组大小非常重要,同时也会讨论关于最优工作组大小的选择和调整的一般方法。

6.1 获取最大的工作组尺寸

在运行完clBuildProgram后,使用下面的API函数可以查询设备的最大工作组尺寸。

size_t maxWorkGroupSize;

clGetKernelWorkGroupInfo(myKernel,

myDevice,

CL_KERNEL_WORK_GROUP_SIZE,

sizeof(size_t),

&maxWorkGroupSize,

NULL );

在clEnqueueNDRangeKernel中使用的实际工作组尺寸不能超过maxWorkGroupSize。如果应用程序没有指定工作组大小,Adreno OpenCL软件可能会选择最大的工作组尺寸。

6.2 需要的和优先的工作组尺寸

每一个kernel函数都有他需要或者优先的工作组大小。对于需要的工作组大小,OpenCL通过下面方法提供给编译器。

  • 使用reqd_work_group_size属性。

作为需求,reqd_work_group_size(X, Y, Z) 属性会传入一个指定的工作组尺寸。如果指定的工作组大小不能满足将会返回一个错误。

比如,如果要求16x16的工作组尺寸:

__kernel __attribute__(( reqd_work_group_size(16, 16, 1) ))

void myKernel( __global float4 *in, __global float4 *out)

{  . . .  }

  • 使用work_group_size_hint 属性

OpenCL会尝试使用这个指定的尺寸,但是不保证真实的大小与指定的一致。比如,提示使用64x64工作组尺寸:

__kernel __attribute__(( work_group_size_hint (64, 4, 1) ))

void myKernel( __global float4 *in, __global float4 *out)

{  . . .  }

在许多情况下,当工作组尺寸严格指定时,编译器不能保证能编译出最优的机器代码。而且,如果片上寄存器不能满足要求的工作组尺寸时,编译器可能会需要将寄存器溢出到系统的RAM内存上。因此,这两种属性并不建议使用,除非必须指定工作组尺寸,kernel才能运行。

注意:为了交叉编译的兼容性,将kernel写成依赖固定工作组的尺寸或者布局,并不是一个好的做好。

6.3 影响工作组最大尺寸的因素

如果没有指定工作组尺寸的属性,一个kernel的最大工作组尺寸依赖以下的几个因素:

  • kernel的寄存器使用。一般来说,kernel越复杂,寄存器使用越多,支持的最大的工作组尺寸越小。过多地使用寄存器的原因如下:

    •   一个工作项中有过多的工作任务。
    •   有控制流
    •   高精度的数学函数(比如,没有使用内部函数或者快速数学运算的编译选项-fastmath)
    •   本地内存,如果需要分配额外的寄存器暂时存储装载和存储指令中源和目的地址。
    •   私有内存,比如为每一个工作组定义了一个数组
    •   循环展开
    •   内联函数
  • 通用寄存器的大小

    •   Adreno低级系列的GPU可能有更少的寄存器数量。
  • n  kernel中的栅栏(Barrier)

    • 如果一个kernel没有使用栅栏(barrier),在Adreno A4x and A5x系列中,在不用考虑寄存器使用的情况下,工作组最大可以设置为DEVICEMAXIUMUM。

6.4 没有barrier的kernels

以前地,一个workgroup中所有的work item要求在同一时间同时驻留在GPU上。对于大量消耗寄存器的kernel,这将会限制他们的最大工作组尺寸,并将会远远小于设备支持的最大工作组尺寸。

从Adreno A4系列起,不需要考虑寄存器的使用情况,没有barrier的kernel就可以有Adreno支持的最大工作组尺寸,一般是1024。对于这种类型的kernel(没有barrier)来说,因为不需要wave之间进行同步,所以当一个旧的wave执行完毕,新的wave就可以开始执行了。

在某些情况下,拥有最大的workgroup尺寸并不意味着他们有最好的并行性。一个没有barriers的kernel可能会因为太复杂导致只有很少的wave在SP上并行执行,这将会导致性能降低。开发者需要继续优化和减少寄存器使用,不考虑从clGetKernelWorkGroupInfo函数中获取到的最大的workgroup尺寸。

6.5 工作组尺寸的调整

这个部分将会介绍一些在选择最优的工作组尺寸和形状时通用的指导准则。

6.5.1 避免使用默认的工作组大小

如果一个kernel调用没有指定workgroup的尺寸,那么OpenCL会用简单的方法找一个能用的工作组尺寸。开发者必须要要意识到,这种默认的工作组尺寸通常不是最优的。有效的做法是,手动尝试使用不同的工作组大小和维度(2D/3D),然后找出最优的一个。

6.5.2 越大的工作组尺寸,越好的性能?

对于许多kernel来说这是正确的,因为增加工作组尺寸能够允许更多的wave运行在SP上,这样能够更好地隐藏延迟和提升SP的使用。

然而,对于某些kernel来说,增加工作组尺寸可能会导致性能退化。一种情况是,由于不良的数据局部性和访问模式,越大的工作组尺寸将导致越多的cache垃圾。这个数据局部性的问题在使用texture获取时更加严重,因为texture cache比统一的L2 cache要小。最终,决定最优的工作大小和维度的本质是kernel的数据获取。

6.5.3 固定的 vs. 动态的工作组尺寸

为了不同设备之间的性能兼容性,避免假设一个工作组尺寸能够适合所有的设备,避免对workgroup尺寸固定编码。一个指定的工作组大小和维度在一个设备上是最优的,在另一个设备上可能是次优的。因此,给定一个kernel,建议针对kernel能够执行的所有设备统计出不同的workgroup尺寸,然后在运行时对每个设备选出一个最优的。

6.5.1 一维 vs二维 vs三维 workgroup(1D/2D/3D)

kernel的维度可以会影响性能。取决于work item的数据获取方式,在某些情况下,一个2D的kernel可能会在cache上有更好的数据本地性(数据在cache上),导致更好的内存获取和更好的性能。然而在其他情况下,一个2Dkernel比1D会产生更多的cache 垃圾。建议尝试使用不同的维度,从而获取最优的性能。

6.6 关于workgroup的其他话题

6.6.1 全局的work size和填充

OpenCL 1.x 要求一个kernel的全局worksize 必须是workgroup尺寸的倍数。如果应用程序指定的workgroup 尺寸不满足这个条件,那么clEnqueueNDRangeKernel的函数调用将会返回一个错误。在这种情况下,应用程序可以填充全局worksize,保证它是用户指定的workgroup尺寸的倍数。

注意:OpenCL 2.0 取消的这个限制,而且global worksize 并不需要必须是workgroup size的倍数,这种被叫做非归一化的workgroup。

理想情况是,workgroup 尺寸的第一个维度是wave尺寸的倍数(比如说32),这样能充分利用wave的资源。如果不是这种情况,可以考虑填充workgroup的大小来满足这个条件,需要记住,在OpenCL 1.x中,全局的worksize必须填充(保证是workgroup的倍数)。

6.6.2 残酷地寻找

因为workgroup 尺寸选择的复杂性,经验常常是发现最优大小和维度的最好方法。

一种选择是,在程序开始时,使用一个与实际的工作任务相同复杂度(但是一般使用比较简单的任务)的唤醒功能的kernel 去动态的寻找最优的workgroup 尺寸。然后将这个选出来的workgroup尺寸用在实际的kernel中。很多商业的标准检查程序就是使用的这种方法。

6.6.3 在workgroup中避免不均匀的工作负载

一些应用程序可能被写成,在不同工作组中出现不均衡的负载。比如说,基于区域的图像处理的用例中会出现一些区域需要比其他区域多很多处理的情况。这种情况需要避免,因为这会导致性能的不可预测性。另外的,如果单个workgroup任务需要太长时间运行的话,会导致上下文切换变的复杂。

解决这个问题的方法是,使用两个阶段处理策略。第一个阶段可能会收集感兴趣的点和为第二阶段准备数据。工作负载越具有确定性,在不同workgroups中进行均等的分配将会更简单。

6.6.4 工作组的同步

OpenCL 并不能保证workgroup的执行顺序,而且也没有定义一个工作组同步的机制。不建议有需要依赖工作组顺序的程序。

在实际情况下,可以使用atomic 函数或者其他方法,在workgroup之间可以进行有限的同步。比如说,一个应用程序可能分配了一个全局内存对象,这个对象需要被不同的工作组中的workitem更新。一个workgroup可以管理一个由其他workgroup更新的内存对象。通过这种方式,可能会实现有限的工作组同步。

Qualcomm_Mobile_OpenCL.pdf 翻译-6-工作组尺寸的性能优化的更多相关文章

  1. Qualcomm_Mobile_OpenCL.pdf 翻译-7 内存性能优化

    内存优化是最重要也是最有效的OpenCL性能优化技术.大量的应用程序是内存限制而不是计算限制.所以,掌握内存优化的方法是OpenCL优化的基础.在这章中,将会回顾OpenCL的内存模型,然后是最优的实 ...

  2. Qualcomm_Mobile_OpenCL.pdf 翻译-9-OpenCL优化用例的学习

    在这一章中,将会用一些例子来展示如何使用之前章节中讨论的技术来进行优化.除了一些小的简单代码片段的展示外,还有两个熟知的图像滤波处理,Epsilon滤波和Sobel滤波,将会使用之前章节中讨论的方法进 ...

  3. Qualcomm_Mobile_OpenCL.pdf 翻译-5-性能优化的概述

    这章提供了一个OpenCL应用程序优化的总体概述.更多的细节将会在接下来的章节中找到. 注意:OpenCL程序的优化是具有挑战性的.相比初始的程序开发工作,经常需要做更多的工作. 5.1 性能移植性 ...

  4. Qualcomm_Mobile_OpenCL.pdf 翻译-8-kernel性能优化

    这章将会说明一些kernel优化的小技巧. 8.1 kernel合并或者拆分 一个复杂的应用程序可能包含很多步骤.对于OpenCL的移植性和优化,可能会问需要开发有多少个kernel.这个问题很难回答 ...

  5. Qualcomm_Mobile_OpenCL.pdf 翻译-2

    2  Opencl的简介 这一章主要讨论Opencl标准中的关键概念和在手机平台上开发Opencl程序的基础知识.如果想知道关于Opencl更详细的知识,请查阅参考文献中的<The OpenCL ...

  6. Qualcomm_Mobile_OpenCL.pdf 翻译-3

    3 在骁龙上使用OpenCL 在今天安卓操作系统和IOT(Internet of Things)市场上,骁龙是性能最强的也是最被广泛使用的芯片.骁龙的手机平台将最好的组件组合在一起放到了单个芯片上,这 ...

  7. Qualcomm_Mobile_OpenCL.pdf 翻译-1

    1 前言 1.1 目的 这篇文档的主要目的是,向原始设备制造商(OEMs),独立软件供应商(ISVs),第三方开发者们,提供在基于高通骁龙400系列.600系列,和800系列的手机平台和芯片上进行开发 ...

  8. Qualcomm_Mobile_OpenCL.pdf 翻译-10-总结

    这篇文档主要是介绍了关于在Adreno GPUs上优化OpenCL代码的详细方法.文档中提供的大量信息能够帮助开发者理解OpenCL基础和Adreno结构,还有最重要的,掌握OpenCL优化技能. O ...

  9. Qualcomm_Mobile_OpenCL.pdf 翻译-4-Adreno OpenCL的程序开发

    这章将简要讨论一些开发Adreno OpenCL应用程序的基本要求,下面将会介绍如何调试和统计程序性能. 4.1  安卓平台上开发OpenCL程序 目前,Adreno GPU主要是在安卓操作系统和在部 ...

随机推荐

  1. python 逻辑运算符and or

    Python中逻辑运算符与C.C++.Golang等语言不太一样. 简单记录下. 1. 都是真或第一个真,第二个假 >>> a = 1 >>> b = 2 > ...

  2. 小程序的autocomplete

    1.别做单个组件的autocomplete了,很坑,牵扯的坑太多,最后碰到原生组件canvas会让人欲哭无泪 2.单个组件的路走不通,走新页面吧,点击input框,进入到下个页面,搜所后选择,点击完成 ...

  3. iOS 图表工具charts之CandleStickChartView(K线)

    关于charts的系列视图介绍传送门: iOS 图表工具charts介绍 iOS 图表工具charts之LineChartView iOS 图表工具charts之BarChartView iOS 图表 ...

  4. 无界面上(linux)生成测试报告(3)

    无界面上(linux)生成测试报告 1.待jmx文件运行完成后,键入命令进入到jtl文件下: #cd testresult#bin目录下使用此命令,进入到jtl文件下 #jmeter -g pushG ...

  5. delphi 导出excel

    Var FExcel:OleVariant; //excel应用程序 FWorkBook :OleVariant; //工作表 Temsheet:OleVariant; //工作薄 FPicture: ...

  6. PHP操作redis部分命令

    //连接本地的 Redis 服务 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('12345 ...

  7. CPU 使用率低 but负载高

    一.关于负载 什么是负载:负载就是cpu在一段时间内正在处理以及等待cpu处理的进程数之和的统计信息,也就是cpu使用队列的长度统计信息,这个数字越小越好(如果超过CPU核心*0.7就是不正常) 负载 ...

  8. shell脚本判断里面的字符含义

    [ -s FILE ] 如果 FILE 存在且大小不为0则为真. [ -a FILE ] 如果 FILE 存在则为真. [ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真. [ -c ...

  9. tensorflow-2.0 技巧 | ImageNet 归一化

    _MEAN_RGB = [123.15, 115.90, 103.06] def _preprocess_subtract_imagenet_mean(inputs): ""&qu ...

  10. 微信小程序购物车实现

    1,wxml <view class="miniCart-wrap {{isIpx?'is-ipx':''}}"> <view class="miniC ...