原帖地址: Interested in GPU Compute? You Have Choices!
发帖人: timhar01 于2014年6月10日在 ARM Mali Graphics
今年早些时候的GDC上发布的OpenGL ES 3.1版本中,最显著的新增是引入了计算着色器(compute shader)。计算着色器与定点着色器(vertex shader)和片段著色器(fragment shader)相似,同时计算着色器拥有更通用的数据访问方式和计算功能。2012年终发布的面向桌面市场的OpenGL 4.3的规范中已经包含这些功能,但是这是第一次引入到移动平台的应用程式接口中,加入到了与OpenCL,RenderScript等在移动GPU通用计算的竞争。那么这些应用程序接口做什么,我们该如何选择呢?这篇文章希望能给大家这些答案。
当对GPU编写非图形程序时,这些工具有一个共同的目标:提供CPU和GPU之间的接口,这样任务可以利用GPU的资源并行的被执行。设计出足够灵活并且可以让用户充分利用GPU构架优势的工具不是一件容易的事情。GPU的优势是对大量数据尽可能并行的执行一些小的任务,这些小的任务经常是重复百万次,GPU处理像素点的时候就是这样做的,GPU上的计算就是对这种能力的一种通用化,所以不可避免这些工具会有会比较相似。
我们先看一下这些主要的选择...
OpenCL
OpenCL最初被苹果公司开发,后来被Khronos Group组织管理,OpenCL规范在2008年底的时候被发布。OpenCL是个一个灵活的框架,可以面的不同类型的处理器,从各种CPU,GPU到DSP,使用这些处理器需要有这些处理器适配的OpenCL驱动。
一旦拥有这些,一个好的OpenCL程序可以兼容其他兼容的平台。
说OpenCL是灵活时,我可能还保守了点。由于基于C99的一个改编,OpenCL非常的灵活,可以让复杂的算法适用于广泛的并行计算框架中。OpenCL已经应用非常广泛-上百的平台都提供相应的驱动。这里Khronos Products列出来哪些产品通过了Khronos的一致性测试。
ARM的Mali 系列的GPU提供了对OpenCL的支持。比如Mali-T604在2012年通过了一致性测试。
那么这些灵活性是不是需要代价呢?建立OpenCL任务是一件复杂的事情,可能需要绞尽脑汁。应用程序接口对OpenCL兼容的设备划分成一组下面的子单位:
主机(Host)理论上可以拥有多个OpenCL设备(OpenCL Device),每个OpenCL设备可以拥有不同的计算单元(Compute Unit),每个计算单元可以有不同数目的处理元素(Processing Element)。OpenCL工作组(OpenCL workgroups)-一组被称为任务项的线程(work items)-在这些处理元素上运行。这些具体实现各个不同的平台可以不同,只要结果符合OpenCL的标准,所以程序代码样式需要非常灵活去适应不同潜在的OpenCL设备,这非常的重要,即便是一个很小的OpenCL程序。
ARM Mali SDK(Mali OpenCL SDK - Mali Developer Center Mali Developer Center)提供了一些不错的从一些基本的到比较复杂的例子和教程。
OpenCL最初考虑移动GPU的时候,应用程式接口就考虑了提高性能和降低功耗。这些大部分集中在图片和视频的处理中。比如:这个来自于Ittiam Systems的使用软件VP9解码的例子:
关于关于在移动领域中使用OpenCL的例子,可以关注rmijat的Mucho GPU Compute, amigo!
OpenCL除了它的灵活性,另一个优势是存在有大量的研究和开发活动围绕着它的应用程序接口。有大量的其它的语言-上次统计是70多个-被编译成OpenCL,简化了它的使用,让它更容易的应用到更多其它相似的环境中。很多的不同的CL库和大量使用不同的语言的框架都提供了对OpenCL应用程序接口的支持。比如PyOpenCL,提供了从Python到OpenCL的支持。Anton Lokhmotov的这边博客Introducing PyOpenCL是介绍PyOpenCL的。
由于需要这些前提条件,只有当任务比较大时才值得去建立一个OpenCL的任务到GPU的管线,也就是建立的代价相对于工作相对于小的时候。Ittiam system最近关于HEVC和VP9软件视频解码就是一个很好的例子。由于不是所有的算法都适合GPU,Ittiam不得不把任务分拆放到CPU和GPU上去执行。他们发现运动估计Motion estimation - Wikipedia, the free encyclopedia是算法中最能充分利用GPU并行性计算的部分。这个算法作为一个整体,被异构化的分配到了CPU和GPU上,降低了CPU的负载而且减少了整体的能耗。这里Ittiam Systems - Mali Developer Center Mali Developer Center有更多关于Ittiam Systems的介绍。大部分的应用程序接口都面向大部分的构架,你在一个平台上的优化可能需要在另一个平台上重新调整,但OpenCL的这种可以使用并且充分利用硬件的底层特性的灵活性正是它真正最大的优势。
近期发展
这一年对Khronos组织和OpenCL都是繁忙的一年-有大量的进展。重要的有WebCL 1.0版本的发布,有点像WebGL对于OpenGL ES所做的一样,它提供了一套基于Javascript的计算应用程序接口,把这种计算扩展到浏览器。当然,浏览器需要一些时间去提供支持-就像支持WebGL一样-但是这是OpenCL在扩大它的吸引力的一个标志。
OpenCL总结
OpenCL提供一个工业标准的应用程序接口,它可以让开发者针对支持的平台底层构架特性做优化,在社区中有大量的开发者和资源可以提供帮助。如果你开发的平台支持它,OpenCL会是个强大的工具。
RenderScript是Google开发的并隶属于Google的计算应用程序接口。自2011年7月Honeycomb发布以来,它一直是Android OS的正式部分。那个时候,它被计划成为一个图形和计算应用程序接口,但是图形功能部分后来被裁减掉。与OpenCL有很多相似之处...基于C99, 有1,2或3维相同的数据结构的概念。rmijat的文章GPU Computing in Android? With ARM Mali-T604 & RenderScript Compute You Can!或者Google关于RenderScript的简介RenderScript | Android Developers提供了快速了解RenderScript。
RenderScript的开发相对于来说比较直接。编写基于C99的RenderScript的代码和用Java来编写剩下Android程序框架部分。Android SDK会生成Java代码可以把这两部分连接起来,并且把它编译成二进制代码,一种中间的设备无关的格式,和APK一起打包。当设备运行时,Android会决定哪些RenderScript设备可用,并且可以运行这些二进制代码。这些设备可能是GPU(比如Mali-T604)或者一个DSP设备。如果这用的设备被找到,这些二进制代码会被传给驱动,驱动再把它们转化为对应的机器指令。如果没有找到合适的设备,Android会回到让CPU执行这些RenderScript任务。
使用这种方式,RenderScript能保证能在任何Android设备上运行,就算最后回到CPU上执行,它还是可以提供一定程度的加速。所以如果你只是需要在Android中对计算加速,RenderScript是个不错的工具。
最早提供GPU加速的RenderScript设备是Google的Nexus10,它使用了ARM Mali-T604的芯片。早期的RenderScript实例显示GPU加速效果很明显。作为一个比较新的应用程序接口,RenderScript的例子相对于OpenCL不是那么好找,不过这些实例会越来越多。这里RenderScript in the Android Support Library | Android Developers Blog有更多关于使用RenderScript的信息。
RenderScript总结
RenderScript在众多的Android设备中提供了一种计算加速的方式。这些计算会不会放到GPU上取决于这些设备和这些设备是否提供这种功能的驱动,就算没有这种功能,RenderScript在CPU上的运行也能提供一定程度的加速。它是一种比OpenCL更高层的应用程序接口,更少的配置选项,所以也更容易掌握,特别的是RenderScript开发集成在了现在的Android SDK中。只需要有Android SDK,你就拥有所以需要的工具。
下面介绍OpenGL ES3.1中新引入的计算着色器(Compute Shaders)。如果之前你熟悉OpenGL ES中的顶点着色器和片段着色器,你会很快熟悉计算着色器。它们采用GLSL(OpenGL着色语言)来编写,拥有相同的状态,uniform和其他特性,可以访问相同的数据类型,包括纹理,image类型,原子计数等。但是,不会像定点着色器和片段着色器那样被编译到同一个程序对象中,所以不是渲染管道的一部分。
计算着色器引入一种新的通用目的的数据缓冲区,着色存储缓冲区对象(Shader Storage Buffer Object),模仿了OpenCL和RenderScript中任务元素和任务组的想法。其他对GLSL的新增是让工作元素知道他们的在数据中的位置而且程序员可以指定工作组的大小和形状。
比较典型的用法是在主渲染线程之前使用计算着色器,把它的结果当作顶点着色器或者片段着色器的输入。
尽管不是渲染管道的一部分,计算着色器也可以用来为它提供支持。它也不是很适合像OpenCL和RenderScript那样提供通用计算的目的-如果你的应用适合使用它,计算着色器提供了一种简单的方式去使用GPU的通用计算能力。
sylvek的博客Get started with compute shaders是一片很好的介绍计算着色器的文章。
计算着色器总结
计算着色器即将来临,多块取决于OpenGL ES3.1的被接受和支持。接下来几年中端机的GPU都会提供对OpenGL ES 3.1的支持,这将是很大的机会。同样的事情发生在OpenGL ES1.1到OpenGL ES2.0的升级,现在你很难找到不支持OpenGL ES2.0的手机和平板。使用简单和即将得到平台的广泛支持会是它最大的优势。
参考Plout Galatsopoulos关于ARM文章ARM submits conformance for OpenGL ES 3.1-和tomolson 的文章Here comes OpenGL® ES 3.1!,可以比较好的了解OpenGL ES3.1.
另外......
OpenGL ES 2.0 片段着色器和帧缓冲区对象
尽管不被用户当作一个很重要的选择,片段着色器在很长一段时间被用作一定程度的通用计算-它在它们有一个优势:广泛支持。任何支持OpenGL ES2.0的GPU-如今基本上所有的智能设备都支持-可以运行片段着色器。这种方法考虑纹理时不一定对应一组纹理元素,而是一维或者二维的数据。只要这些被着色器读写的数据能被相应的纹理格式表示,这些数据就可以被采样和被写入到数组中。你只需要建立一个帧缓冲区对象,在上面画一个四边形(两个三角形组成一个矩形),使用一个或者多个数据数组当作纹理输入。片段着色器能从这些纹理中做它想要的计算,把结果输出到帧缓冲对象中。被生成的纹理结果可以被使用到后面的渲染管道中的片段着色器中。
总结
这片文章中,我们谈到了OpenCL,RenderScript,计算着色器和片段着色器可选去利用GPU来做非图形的计算任务。每一种方法都有自己的特性来满足某些开发者的需求,所有这些方法都可以提高性能同时降低功耗。文章到这里该结束了。嵌入式和移动平台提供的各种计算发展很快。好消息是Mali GPU构架被设计来支持最新最前沿的计算应用程序框架,让我们的客户可以在不同平台不同系统中提高性能降低功耗。