在recent SO question上,我解释了如何多次调用RenderScript内核将有效地强制所有线程在调用之间全局同步。
我目前正在处理按顺序应用于图像数据的多个卷积。由于卷积算法需要读取输入图像的周围像素数据,因此我实现了一个工作流程,其中我自己的自定义内核被多次调用 - 为了确保在每个步骤中,来自先前卷积的所有数据都已准备就绪且可在正确的坐标。到目前为止,这项技术对我来说非常有用。
然而,在我不断寻求优化的过程中,我注意到通过在线程的本地寄存器中保存中间值而不是在内核调用之间将它们写回全局内存分配,可以获得很多性能。如果我能够以这种方式链接这些卷积,事情会更快地运行很多。问题显然是访问周围线程的寄存器实际上是不可能的。此外,这将要求线程同步运行,以确保阶段之间的这些中间值按预期顺序计算。
在CUDA和OpenCL中,这些问题非常普遍,并且通过众所周知的屏障同步+共享内存切片技术来解决,这些技术又依赖于CUDA线程块或OpenCL工作组的概念。我相信这些概念在RenderScript中是不存在的,因为这个问题与桌面级GPU和移动SoC之间截然不同的架构密切相关。
所以我这里显而易见的问题是,在RenderScript中这样的事情是否可行?也就是说,更好地管理线程和可能的线程组,以便更快地共享数据。
在Jason Sams和Tim Murray的Google I/O 2013 RenderScript talk上,讨论了脚本组如何能够做一些幕后优化,例如跨设备并行化,内存平铺和内核融合;所有这一切都是通过在运行时分析组中的依赖关系DAG,并在需要时自动创建分配或可能优化它们。我假设这最后一点引用融合内核,以便它们可以处理它们自己的本地数据,我上面提到的如何将数据保存在本地寄存器中并在单个内核中组合单独的步骤。
所有这些似乎与我正在寻找的非常一致,特别是因为我的应用程序确实是一个定义明确的DAG相互依赖的操作(对于卷积神经网络)。因此,如果脚本组确实是这些机制的可靠的以移动为中心的替代方案,我想知道是否有任何方式可以影响这些优化的发生方式和位置。或者如果没有,运行时可以信任运行多少,以便在硬件运行时从我的数据依赖性中做出正确的推断 - 在卷积算法的“周围”像素数据访问的特定情况下。
我意识到这可能仍然在pogress中工作,并且方法在这一点上将高度依赖于硬件。因此,如果目前还没有针对此类问题的直接解决方案 - 我非常愿意接受关于RenderScript在未来版本中可能会如何处理此类工作流程的推测性答案。
我对这方面的一些见解非常感激,因为它会极大地影响我自己项目的发展方向,更不用说那里肯定会有很多其他人想知道这样的一般并行计算任务是怎样的在RS处理。
非常感谢!
答案 0 :(得分:3)
正如您所发现的那样,RS无法直接在线程之间共享数据。但是,您所描述的内容可以使用ScriptGroup
完成。问题在于,组中的每个脚本都必须是唯一的,因此您无法反复提供相同的脚本。至少,不是现在写的。您当然可以将脚本的“核心”放在RS头中,并将其包含在多个内核中。 ScriptGroup
允许您将一个脚本的输出变为另一个脚本的输入,或者一个脚本的输出变为另一个脚本的全局字段。文档指出内核到内核(输出到输入)是更有效的用例。使用此方法,您的同步问题将得到解决,因为引擎将在启动第二个脚本之前针对整个输入数据集执行第一个脚本,等等。脚本本身将针对硬件进行适当的并行化(使用CPU或GPU / DSP) )。引擎不必在脚本之间弹回Java,也可以根据需要管理幕后的数据分配。
您可能会注意到ScriptGroup
利用Script.KernelID
或Script.FieldID
以确定连接两个内核的确切脚本或字段。只要您使用RS编译器属性pragma显式调出内核函数,您的自定义脚本就会自动生成这些内容。然后你可以调用getKernelID_
(其中'name'是你脚本中的内核函数名)来获取内核ID。<name>