分享

拿起SIMD的武器II

 Foxmouse 2012-10-09

前瞻-拿起SIMD的武器II

上篇文章前瞻-主流处理器中的数据并行支持(SIMD)>和《前瞻-拿起SIMD的武器I》分别介绍了当今主流CPU中的SIMD扩展 ,以及前人是如何利用SIMD来做优化的,本文<前瞻-拿起SIMD的武器II>将探讨如何使用CPU的向量指令为程序做优化

如何实现?

编程环境

在现在CPU设计中都加入SIMD扩展并不是解决应用性能问题的好方法。如果没有很好的利用途径,再强大的SIMD扩展指令集都是徒劳。接下来,我们从编译器技术和编程方法论上探讨如何使用SIMD指令来实现应用加速。

编译器的局限

尽管现在SIMD扩展在大部分处理器中都已具备,但目前编译器仍难生成基于SIMD指令的高效代码序列,因为从以控制流为主体的C语言中自动抽象并 行化很难。仅仅有几个商业编译器能自动产生SIMD并行,但往往需要很高的代价,结构也很不令人满意。这一块依然是研究开发的热点。SWAR(SIMD within a register)是曾经比较典型的比那一起。一开始作为一个兼容面向多种处理器,之后又转化为一个能识别简单高层并行语言SWARC(一个为使用 SIMD指令而生的数据并行C语言).但是,因为SIMD指令的限制,数据并行语言的成功取决于应用领域中高效的并行方法和能很好描述这种应用的语言。 SWARC在语言设计阶段,缺乏对特定并行方法的假设使得它使用范围很有限。
也有人针对图像处理任务,提出了一种对C语言的并行扩展和对应的SIMD编译器,并用它实现SIMD加速。作者使用这种语言实现了图像滤波器,并使用 SIMD编译器在奔腾处理器上得到了图像滤波器的高效代码序列。通过与最新C编译器中编译得到的代码对比,SIMD编译器得到的代码有4x的加速.也有人 专门就如何降低用户学习新编程语言的复杂性进行研究,并提出了一个编程语言原型和相应的编译器。自动向量化的工作也有人在继续奋斗。有人提供了在编译C代 码时自动使用SIMD指令代替的技术,使用它让跨平台的源代码使用SIMD指令。这种技术基于经典的代码树结构,并在其上收集数据流图信息以便生成 SIMD指令,对目前的主流处理器,这样实现还行,但是很多新的SIMD结构都在趋于提供较高层的并行了。
在某些应用中,通过使用编译器内部的函数来插入一组SIMD指令实现数据在寄存器中的重排,可以实现更高度的并行。这种编译器内部函数都对应一个或多个特 定的SIMD指令,通过插入这些函数,程序员告诉编译器需要选择哪个SIMD指令,编译器则负责寄存器分配和指令调度。最近有人描述了SIMD指令如何从 常规代码中产生,如何决定SIMD操作的书讯,并每个SIMD指令组。在该提议中,独立的存储访问都组织称SIMD操作,SIMD数据流图根据访存的数据 依赖构建,接着SIMD操作的顺序复制到数据六图中。这种机制并不依赖特定的体系结构,在SSE实现该提议,对于某些应用核心,加速比达到35%。
另外也有人研究了使用SIMD并行操作和使用串行操作的能耗问题。通过比较多个benchmark,作者的结论是:使用SIMD指令能节省72%的能耗,提升76%的性能。

编程方法论

虽然没有编译器令人满意的支持,但SIMD依然是有效的提升性能的方法,接下来介绍三种使用SIMD扩展的途径。

  1. 汇编语言:这是最高效的使用方式,因为这种编程方式能直接从硬件平台获得性能。但很容易出错,而且可移植性不强。
  2. 共享库:这些库通常处理器生产商提供的,但这些库只能覆盖特等的函数,而且只能用在特定的处理器上。比如,Intel的汇编库就提供了很多常用信号处理,向量计算和图像处理的汇编函数库供C函数调用,但通常,这种方式会造成库中的可用函数和目标应用需要的不匹配。
  3. 向量化编译器:最理想的情况下,高级语言编译器能自动识别代码中的并行部分,并生成相应的SIMD指令串。但是虽然现在有很多自动SIMD向量化 的提议,但成功的很少。值得一提的是Intel C++ Compiler中的intrinsic支持,把括MMX,SSE,SSE2的SIMD指令支持,C++ SIMD向量类,还有一个自动的向量化工具能自动从循环中得到SIMD并行。它为Microsoft Windows和Linux操作系统环境带来明显性能提升。ICC的二进制文件,在Windows环境下能与Visual C++二进制兼容,在Linux能与GCC相应版本的二进制兼容。现在通过intrinsic库和C++来编程要比汇编语言容易的多,因为不再需要维护寄 存器,可以方便的开发较大的软件,但因为编译器要完成寄存器分配和指令调度,所以相对于汇编,性能可能会有下降,但并不绝对。 GNU Gcc 4.2也支持了包括AtliVec,SSE2和3DNow!在内的一些多媒体扩展。

数据对齐

对齐就是把数据和代码放在主存中能让硬件更高效访问的地址的技术,也就是存储地址的一种属性。一个地址是X对齐的,如果它的地址能表示为Xn+0的 形式。其中X是2的某次幂,n为任意整数。一个存储向量数据的内存块,若其边界是非对齐的,那访问它的开销将会非常大,因为需要使用数据重排机制。这种机 制通常需要产生很多额外的存储访问。为了避免这种过高代价,已经有了一些技术,如循环分割,动态/静态对齐检测。很多CPU,如基于Alpha, IA-64, IA-32,MIPS和SuperH的结构,都禁止访问非对齐数据,如果程序请求访问了非对齐的数据,则抛出异常。

数据对齐是开发自动使用SIMD扩展必须要解决的基础问题之一。掌控对齐会大大增加自动向量化的难度,并限制其应用范围。为了能对向量数据高效存储,存储访问必须正确对齐。

选择合适的SIMD扩展

SIMD扩展在现在的PC中几乎都有,为啥不用呢?但,再者之前,得先回答几个问题:

  1. 现在的代码能从MMX,SSE,SSE2或SSE3中获益吗?
  2. 代码是整点运算还是浮点?
  3. 需要什么样的编程技术?
  4. 如何组织数据类型,如何对齐?

另外,为了能从SIMD扩展中获得最大收益,下面几点一定要好好评估:

  1. 计算敏感的程序段
  2. 足够给程序性能产生影响的程序段
  3. 几乎没有数据依赖控制流图的程序段
  4. 需要浮点运算的程序段
  5. 使用少量指令就能编写的计算代码段
  6. 需要额外调整才能高效利用cache结构的代码段

一款商业软件Intel VTune能较好到检测出可以并行的程序段。协助程序员优化性能。

总的来说,一个能做SIMD优化的程序段,应该是循环体较小,循环操作一个数组,该数组由8、16或者32位对齐整数,单精度32位浮点,或双精度 64位浮点组成。再加上使用尽可能少的数据类型,更长的向量数据,并且小心的控制内存访问。能做到这些,SIMD扩展就能为你的程序带来不小的性能提升 了:)

个人总计:

至此,对目前最低级的并行–单指令多数据并行。算是有了点认识。SIMD几乎所有的主流CPU上都有,从Alpha的MVI,IBM PowerPC上的AltiVec和SPE,HP PA-RISC 多媒体加速扩展(MAX), Intel的MMX,iwMMX,SSE,SSE2,SSE3,SSSE3;AMD的3DNow!, ARC的ARC视频处理子系统,SPARC的VIS, Sun的MAJC, ARM的NEON, MIPS的MDMX和MIPS-3D, Cell的SPU, Philips的Xetal 到GPU上SIMD的普遍使用,从64位到128位,256位的数据并行。SIMD的硬件支持越来约强大。

另一方面,高品质的多媒体处理需求,也使得我们的计算机应用中数据处理越来越庞大。但唯独编译器很难为SIMD加速做贡献。SIMD的并行需要很弱 的数据依赖,数据流的统一。而这种统一很难靠编译器来提取。高效的方式还得靠人工来做。人工来做,就意味着人要了解他要优化的这个算法,哪里数据计算比较 多,哪里跳转,控制语句比较多。能不能使用SIMD优化,如何使用SIMD优化。

SIMD的硬件支持因为各个CPU厂商的设计目标不同,而且还有知识产权的问题。这样SIMD标准很难统一起来。针对每一个特定的CPU型号,都要做特定的SIMD优化,这也给软件的可移植性带来了挑战。

编译器能提取数据依赖吗?编译器能为程序员做点什么?编译器能在多大程度上协助程序员的工作?主流编译器已经有了对MMX,SSE,SSE2的 intrinsic支持,编程在稍微损失性能的条件下编程更加容易了。Intel的VTune能帮助我们更好的发现并行机会。但很明显,这些还不够。幸 好,GCC也引入了Graphite,或许在不久的将来,会有一些机会吧。现在影响SIMD,最多的不是找到一系列指令串来操作数据,而是如何高效的把数据组织成一个向量数据,在计算完成以后,高效的把这些数据都拿出来。

未来,我们还有很多值得期待,还有许多可以努力!

0

相关文章:

6 thoughts on “前瞻-拿起SIMD的武器II

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多