新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 在Cortex-A8平台下memcpy ARM/NEON汇编性能的测试

在Cortex-A8平台下memcpy ARM/NEON汇编性能的测试

作者:时间:2016-11-10来源:网络收藏
本文介绍了基于ARMv7架构的Cortex-A8芯片(FreeScale i.MX51 / i.MX53/QualComm msm8x50 / msm7x30/Samsung s5pc100 / s5pc110/TI omap 3430 / omap 3730芯片)上采用C语言、ARM汇编和NEON汇编实现的memcpy的性能对比,并输入分析了NEON指令(不同处理器的NEON内存位宽从64-bit到128-bit不等)和cache的预取preload(preload engine指令)对性能的影响。最终结论表明1.在拷贝块大小block size = 512B ~ 32K之间,有一个性能高台,block size = 256K也有一个性能的转折。这个特性体现了芯片32KB L1 / 256KB L2 cache的影响;2. NEON指令的性能总是要高于ARM指令的性能。随着发展ARM/NEON指令之间性能差在缩小。交替使用ARM/NEON指令,性能往往要差于NEON版本;3.如果没有很好的模型设计,软件去干预cache的使用,很容易会造成性能的恶化; 4.在fit in cache条件下,Snapdragon平台有最好的性能; 5.在out of cache条件下,s5pc110有最好的性能; 6.在同一个硬件平台下,超频对memory性能影响很小; 7.同一种的实现,在不同的硬件平台上都有不同的表现。没有一种实现在所有平台上是最好的。
  1. 前言

    在C run time library中,memcpy是重要的函数,对应用软件的性能有着重要的影响。ARM芯片发展到Cortex-A8[1][2]架构,不但频率有了很大提升,而且架构设计有了很大地改进。其中增加的NEON指令,是类似于原先X86平台下的MMX指令,是为多媒体而设计。但因为这类指令一次可以处理64-bit数据,对memcpy函数性能提升也很有帮助。本文主要是测试采用NEON[2]指令的多种memcpy实现,探讨NEON指令和预取(preload)指令对性能的影响,以及在芯片优化和工艺进步后,这些影响的变化趋势。同时希望芯片设计人员在了解软件实现的基础上,给予一个知其然,也知其所以然的解释,进而指导进一步提高性能的方向。

    本文引用地址:http://www.eepw.com.cn/article/201611/317414.htm
  2. 平台介绍

    本次的测试平台来源于笔者工作项目中接触到的Cortex-A8平台。见下面列表:

  • FreeScale i.MX51 / i.MX53
  • QualComm msm8x50 / msm7x30
  • Samsung s5pc100 / s5pc110
  • TI omap 3430 / omap 3730
  1. i.MX5 family

    i.MX5 family的介绍见[6][7]。其中i.MX535可以运行在800MHZ / 1000MHZ两种频率上。

  • i.MX515
    • freq: 800MHZ
    • cache size: 32KB/32KB I/D Cache and 256KB L2 Cache
    • cache line: 64-bit wide(NEON), 64-byte / line
  • i.MX535
  • freq: 800MHZ / 1000MHZ
  • cache size: 32KB/32KB I/D Cache and 256KB L2 Cache
  • cache line: 64-bit wide(NEON), 64-byte / line
  1. Snapdragon family

    Snapdragon的介绍见[8][9][10]。其中msm7x30可以运行在800MHZ / 1000MHZ两种频率上。此外Snapdragon cache特别之处是128-bit wide(NEON), 128-byte / line。标准Cortex-A8中,该数值为64-bit wide(NEON), 64-byte / line。这对性能有较大影响。

  • msm8x50
    • freq: 1000MHZ
    • cache size: 32KB/32KB I/D Cache and 256KB L2 Cache
    • cache line: 128-bit wide(NEON), 128-byte / line
  • msm7x30
    • freq: 800MHZ / 1000MHZ
    • cache size: 32KB/32KB I/D Cache and 256KB L2 Cache
    • cache line: 128-bit wide(NEON), 128-byte / line
  1. s5pc family

    s5pc family参考平台见[11]。

  • s5pc100
    • freq: 665MHZ
    • cache size: 32KB/32KB I/D Cache and 256KB L2 Cache
    • cache line: 64-bit wide(NEON), 64-byte / line
  • s5pc110
    • freq: 1000MHZ
    • cache size: 32KB/32KB I/D Cache and 512KB L2 Cache
    • cache line: 64-bit wide(NEON), 64-byte / line
  1. omap3 family

    omap3 family参考平台见[12][13][14]。

  • omap3430
    • freq: 550MHZ
    • cache size: 16KB/16KB I/D Cache and 256KB L2 Cache
    • cache line: 64-bit wide(NEON), 64-byte / line
  • omap3730
    • freq: 1000MHZ
    • cache size: 32KB/32KB I/D Cache and 256KB L2 Cache
    • cache line: 64-bit wide(NEON), 64-byte / line
  1. memcpy实现介绍

    memcpy的实现在ARM平台上的发展有3类版本:

  2. C语言版本
  3. ARM汇编版本
  4. NEON汇编版本

    ARM公司的文档[4]对memcpy的实现有很好描述。有人[5][19][20]还进一步阐述了实现原理和技巧。简述如下:

  • NEON指令一次可以处理64-bit数据,效率更高。
  • NEON架构与L1/L2 cache都有直连,在OS层级enable后,可以获得更好的性能。
  • ARM / NEON的pipeline有可能异步处理,交替使用ARM / NEON指令有可能获得更好的性能。
  • 在一次循环中,用尽可能多的寄存器copy更多的数据,保证pipeline有更好的效率。目前一次最大处理块为128-byte。
  • 对cache的操作有讲究。
    • memcpy属于一次扫瞄无回溯的操作,对于cache采用预取(preload)策略可以提高hit rate。所以汇编版本中一定会使用pld指令提示ARM预先把cache line填充好。
    • pld指令中的offset很有讲究。一般为64-byte的倍数。在ARMv5TE平台是一个循环用一个pld指令。在Cortex-A8平台上速度更快,需要一个循环用2~3个pld指令填充cache line。这样一个循环消费2~3个时钟周期换得cache hit rate提高,效果是值得的。
    • 进一步的,Cortex-A8架构提供了preload engine指令,可以让软件更深地影响cache,以便让cache hit rate得到提高。不过要在用户空间使用ple指令,需要在OS中打补丁开放权限。

  1. C语言版本

    C语言版本主要是做对比。采用两个实现:

  2. 32-bit wide copy。后面标记为in32_cpy。
  3. 16-byte wide copy。后面标记为vec_cpy。这个实现的技巧是采用gcc的向量扩展"__attribute__ ((vector_size(16)))",在C语言层级实现16-byte wide copy,将具体实现交给编译器。

    值得注意的事情是,编译器不会主动插入pld指令。因为编译器无法判断应用对内存的访问模式。

  4. ARM汇编版本

    ARM汇编版本也主要是做对比。采用两个实现:

  5. Siarhei Siamashka实现[15]。后面标记为arm9_memcpy。他是为Nokia N770做的优化。
  6. Nicolas Pitre实现[16]。后面标记为armv5te_memcpy。这是目前glibc里面缺省的arm memcpy实现。
  7. NEON汇编版本

    NEON汇编版本采用四个实现:

  8. M?ns Rullg?rd实现[19]。这是一个128-byte-align block的最简单的实现。没有判断不是128-byte align的情况。因此不是实用的版本。但通过这类实现,可以考察memcpy性能的极限。他总共提供4种实现。
  9. 全ARM汇编的实现。后面标记为memcpy_arm。此外,笔者还将其中的pld指令去掉,做为对比试验,考察pld指令的影响。后面标记为memcpy_arm_nopld。
  10. 全NEON汇编的实现。后面标记为memcpy_neon。此外,笔者还将其中的pld指令去掉,做为对比试验,考察pld指令的影响。后面标记为memcpy_neon_nopld。
  11. ARM / NEON指令交替使用的实现。后面标记为memcpy_armneon。此外,笔者还将其中的pld指令去掉,做为对比试验,考察pld指令的影响。后面标记为memcpy_armneon_nopld。
  12. ple + NEON的实现。后面标记为memcpy_ple_neon。此外,笔者还将其中的NEON指令换成ARM指令,做为对比试验,考察ple指令对ARM/NEON指令的影响。后面标记为memcpy_ple_arm。因为这个实现需要对linux kernel打补丁,在omap3430平台上没有成功。在Snapdragon平台上更换kernel有些麻烦,所以也没有测试。
  13. CodeSourcery实现[17]。这是CodeSourcery toolchain中的glibc里面的实现。也分两种实现。
  14. ARM实现。后面标记为memcpy_arm_codesourcery。笔者还将其中的pld指令去掉,做为对比试验,考察pld指令的影响。后面标记为memcpy_arm_codesourcery_nopld。
  15. NEON实现。后面标记为memcpy_neon_codesourcery。这也是Android bionic里面采用的NEON实现。笔者还将其中的pld指令去掉,做为对比试验,考察pld指令的影响。后面标记为memcpy_neon_codesourcery_nopld。
  16. QualComm实现[18]。后面标记为memcpy_neon_qualcomm。这是QualComm在Code Aurora Forum中为Snapdragon平台开发的优化版本。主要是对8660/8650A平台的优化。这个版本的特点是针对L2 cache line size=128bytes而设计,pld offset设置得特别大。结果在其它Cortex-A8平台上没有效果。所以笔者将pld offset改为M?ns Rullg?rd实现的数值。笔者还将其中的pld指令去掉,做为对比试验,考察pld指令的影响。后面标记为memcpy_neon_qualcomm_nopld。
  17. Siarhei Siamashka实现[20]。后面标记为memcpy_neon_siarhei。这是Siarhei Siamashka向glibc提交的NEON版本,没有被glibc采纳。但是在MAEMO项目中得到采用。这个版本的特点是pld offset是从小到大增长的,以期望适应block size的变化。
  18. 测试方案介绍

    测试方案十分简单。参考了movial memory tester的实现[21]。执行步骤如下:

  19. 先对每个实现进行正确性的验证。主要方法是以随机的block size & offset,填充随机的内容,然后执行memcpy操作,然后再用系统的memcmp函数对两块内存做校验。
  20. 然后对每个实现以不同的block size调用400次。如果total copy size < 1MB,则增加count直到满足要求。对总操作计时。
  21. 以total copy size / total copy time公式计算memcpy bandwidth。

    上述提到的block size = 2^n ( 7 <= n <= 23 )。

    此外,这个测试程序运行在openembedded-gpe软件系统中。QualComm / Samsung硬件平台只提供Android软件系统,要更换到GPE系统有些麻烦,则采用chroot方式进行测试。不论是哪种软件平台,都是进入到图形系统后,静置,等待黑屏,然后再进行测试。

    下表是运行环境的统计。

    硬件平台

    软件环境

    imx51 800MHZ

    openembedded-gpe

    imx53 1000MHZ

    openembedded-gpe

    imx53 800MHZ

    openembedded-gpe

    msm7230 1000MHZ

    Android + chroot

    msm7230 800MHZ

    Android + chroot

    msm8250 1000MHZ

    Android + chroot

    omap3430 550MHZ

    openembedded-gpe

    omap3730 1000MHZ

    openembedded-gpe

    s5pc100 665MHZ

    Android + chroot

    s5pc110 1000MHZ

    Android + chroot

    下表是测试项目的统计。

    实现方案

    i.MX51

    i.MX53

    Snapdragon

    s5pc1xx

    omap3430

    omap3730

    int32_cpy

    YES

    YES

    YES

    YES

    YES

    YES

    vec_cpy

    YES

    YES

    YES

    YES

    YES

    YES

    arm9_memcpy

    YES

    YES

    YES

    YES

    YES

    YES

    armv5te_memcpy

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_arm

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_arm_nopld

    YES

    NO

    YES

    YES

    YES

    YES

    memcpy_neon

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_neon_nopld

    YES

    NO

    YES

    YES

    YES

    YES

    memcpy_armneon

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_ple_arm

    YES

    YES

    N/A

    YES

    N/A

    YES

    memcpy_ple_neon

    YES

    YES

    N/A

    YES

    N/A

    YES

    memcpy_arm_codesourcery

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_arm_codesourcery_nopld

    YES

    NO

    YES

    YES

    YES

    YES

    memcpy_neon_codesourcery

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_neon_codesourcery_nopld

    YES

    NO

    YES

    YES

    YES

    YES

    memcpy_neon_qualcomm

    YES

    YES

    YES

    YES

    YES

    YES

    memcpy_neon_qualcomm_nopld

    YES

    NO

    YES

    YES

    YES

    YES

    memcpy_neon_siarhei

    YES

    YES

    YES

    YES

    YES

    YES

    注1:因为i.MX53 EVK板子发生故障,未能测试所有no pld的测试项。

    注2:在给omap3430打开preload engine后,测试产生非法指令错,未能测试ple的测试项。

    注3:要替换Snapdragon kernel有些麻烦,未能测试ple的测试项。

  22. 测试结果与分析

    下面的图表限于页面大小不能很好地显示细节。具体的数据和大图可到数据表文档中查看。

  23. 各个硬件平台上各种实现的表现

  24. imx51 800MHZ

  25. imx53 1000MHZ

  26. imx53 800MHZ

  27. msm7230 1000MHZ

  28. msm7230 800MHZ

  29. msm8250 1000MHZ

  30. omap3430 550MHZ

  31. omap3730 1000MHZ

  32. s5pc100 665MHZ

  33. s5pc110 1000MHZ

    1. 小结

  34. 在block size = 512B ~ 32K之间,有一个性能高台,block size = 256K也有一个性能的转折。
  35. 这个特性体现了32KB L1 / 256KB L2 cache的影响。
  36. 小于512B的性能不佳,可能与函数调用,函数开始的块对齐技巧造成的损耗有关,也可能与block size太小,cache没有准备好函数就结束了有关。
  37. 文档[]对memcpy的实现还是有指导意义的。但随着芯片内部的优化和工艺的提升,有些规则发生了变化。
  38. NEON指令的性能总是要高于ARM指令的性能。但交替使用ARM/NEON指令并不总是带来性能的提升。随着发展ARM/NEON指令之间性能差在缩小。
  39. pld指令的作用越来越小。在较老的芯片上,如omap3430,采用pld指令后,同一个实现可以有50%的性能提升。在较新的芯片上,如msm7230/s5pc110上,性能基本没有区别,甚至同一个实现没有pld指令后,性能稍稍有些提升。这也许是因为pld指令没有效果,倒反在每个循环中浪费了时钟周期造成的。
  40. 采用ple指令的实现的性能令人大失所望。这也说明如果没有很好的模型设计,软件去干预cache的使用,很容易会造成性能的恶化。
  41. Snapdragon平台有最好的cache性能。超出cache后,各种实现(包括C语言实现)的性能基本一致,也很高效。这也许是Snapdragon平台13-stage load/store pipeline[][]的设计造成的。这个特性对高级语言是有好处的。因为编程不可能在很多地方采用汇编语言。这样开发人员就不必过多地考虑汇编优化,依赖编译器就可以了。
  42. s5pc110平台有最好的平均性能。超出cache后,NEON实现的性能最好,基本保持一条水平线。
  43. 在small/big block size下各个硬件平台的表现

    性能因为block size分为fit in cache / out of cache两种表现,所以做两个剖面做对比分析。

  • 8K block size。体现fit in cache时的性能。
  • 8M block size。体现out of cache时的性能。
  1. M?ns Rullg?rd的实现

    因为M?ns Rullg?rd的实现最简单,除了一个循环体外,没有其它判断代码,可以认为是体现平台速度极限的实现。

  2. ARM的实现

  3. NEON的实现

  4. 小结

  5. NEON指令的性能总是要高于ARM指令的性能。随着发展ARM/NEON指令之间性能差在缩小。
  6. 交替使用ARM/NEON指令,在fit in cache条件下性能要差于NEON版本。在out of cache条件下,两个版本性能基本一样。
  7. 在fit in cache条件下,Snapdragon平台有最好的性能。超过第二名s5pc110大约为43%。
  8. 在out of cache条件下,s5pc110有最好的性能。超过第二名omap3730大约为57%。
  9. 在同一个硬件平台下,超频(如i.MX53 800/1000MHZ & msm7x30 800/1000MHZ)对memory性能影响很小。
  10. 实用ARM/NEON实现在各个硬件平台的表现

    通过同一种实现在不同硬件平台上性能的对比,结合上一节的图表,可以评价一种实现的平均性能,也就是适应性。

  11. ARM的实现

  12. NEON的实现

  13. 小结

  14. 同一种的实现,在不同的硬件平台上都有不同的表现。没有一种实现在所有平台上是最好的。
  15. Codesourcery版本,包括ARM/NEON版本,有很好的适应性。不愧是做toolchain的公司。
  16. Siarhei Siamashka的NEON版本也有很好的适应性。NOKIA的技术实力也很强。这哥们好像也是pixman项目里面做NEON优化的主力。
  17. Qualcomm版本只适合Snapdragon平台。期待以后能在msm8660以及后续的芯片上进行测试。
  18. 总结

  19. 在block size = 512B ~ 32K之间,有一个性能高台,block size = 256K也有一个性能的转折。这个特性体现了32KB L1 / 256KB L2 cache的影响。
  20. NEON指令的性能总是要高于ARM指令的性能。随着发展ARM/NEON指令之间性能差在缩小。交替使用ARM/NEON指令,性能往往要差于NEON版本。
  21. 如果没有很好的模型设计,软件去干预cache的使用,很容易会造成性能的恶化。
  22. 在fit in cache条件下,Snapdragon平台有最好的性能。
  23. 在out of cache条件下,s5pc110有最好的性能。
  24. 在同一个硬件平台下,超频对memory性能影响很小。
  25. 同一种的实现,在不同的硬件平台上都有不同的表现。没有一种实现在所有平台上是最好的。
  26. 进一步的测试

    因为在Cortex-A8系列芯片里,NEON模块是必有的。而在Cortex-A9系列芯片里,NEON模块是可选的。因为NEON模块会影响到die size,因而影响功耗和成本。因此有些Cortex-A9芯片,如Nvidia Tegra250,没带有NEON模块。那么有无NEON模块会对软件性能造成什么样的影响呢?



评论


相关推荐

技术专区

关闭