【问题标题】:ARM Assembler NEON - Increasing performanceARM 汇编程序 NEON - 提高性能
【发布时间】:2012-02-23 14:08:32
【问题描述】:

我已将部分算法从 C 转换为 ARM 汇编程序(使用 NEON 指令), 但现在它比原始 C 代码慢 2 倍。 如何提高性能?

目标是 ARM Cortex-A9。

该算法从数组中读取 64 位值。从这个值中提取一个字节,然后将其用作另一个表的查找值。 这部分进行了大约 10 次,每个结果表值与其他值进行异或运算,并将最终结果写入另一个数组。

类似这样的:

result[i] = T0[ GetByte0( a[i1] ) ] ^ T1[ GetByte1( a[i2] ) ] ^ ... ^ T10[ (...) ];

在我的方法中,我在 Neon Registers 中加载整个数组“a”,然后在 arm 寄存器中移动右字节,计算偏移量,然后从表中加载值:

vldm.64 r0, {d0-d7}         //Load 8x64Bit from the input array

vmov.u8 r12, d0[0]          //Mov the first Byte from d0 into r12
add r12, r2, r12, asl #3    // r12 = base_adress + r12 << 3
vldr.64 d8, [r12]           // d8 = mem[r12]
.
.
.
veor d8, d8, d9             // d8 = d8 ^ d9
veor d8, d8, d10            // d8 = d8 ^d10      ...ect.

其中 r2 保存查找表的基址。

adress = Table_adress + (8* value_fromByte);

这一步(除了开始时的加载)大约完成了 100 次。为什么这么慢?

"vld""vldr""vldm" 之间还有什么区别 - 哪个最快。 如何仅在 Neon 寄存器中执行偏移计算? 谢谢。

【问题讨论】:

  • 我认为您的 C 代码与描述不符。 C 对同一个字的多个字节进行异或运算,但问题说每个字节都用于索引下一个字节。代码显示不清晰,我们无法优化。
  • 是的,你是对的。我编辑了它。它总是另一个词。
  • 霓虹灯可能不是优化这一点的方法。代码是普通ARM指令集中的SIMD。您可以使用 64k 表(易于生成)并一次处理 16 位,也可以一次运行 32 位 EOR 并折叠结果。获取随机索引的算法会限制此内存,因此执行 EOR 的代码不会显着影响事情。

标签: android performance assembly arm neon


【解决方案1】:

Neon 无法处理大于 VTBL 指令限制(如果我没记错的话是 32 字节)的查找。
查找表是如何创建的?如果只是计算,只需让 Neon 进行数学运算,而不是求助于查找。 这样会快很多。

【讨论】:

  • 查找表是静态和硬编码的。大约 2000 个 64 位值。
【解决方案2】:

不要使用

vmov.u8 r12, d0[0]

将数据从 NEON 寄存器移动到 ARM 寄存器是您能做的最糟糕的事情。

也许你应该看到 VTBL 指令! 你的字节范围是 0..255 是多少?

【讨论】:

  • 感谢您的回复,我会尝试更改。我想要归档的是:我有一个二维数组(8x8 字节)。我需要每列一个不同的字节。这个字节值是另一个数组(64 位)的索引。所以我需要得到正确的字节值,在表中查找我的值,然后对所有找到的值进行异或。我希望这或多或少可以理解。
【解决方案3】:

你可以试试

ldrb     r12, [r0], #1
add      r3, r2, r12, asl #3
vld1.64  {d0}, [r3]

ldrb     r12, [r0], #1
add      r3, r2, r12, asl #3
vld1.64  {d1}, [r3]
veor     d0, d0, d1         // d8 = d8 ^ d1

ldrb     r12, [r0], #1
add      r3, r2, r12, asl #3
vld1.64  {d1}, [r3]
veor     d0, d0, d1         // d8 = d8 ^ d1

...

这不是最好的解决方案。之后,您可以通过重新订购指令来提高性能。

【讨论】:

  • 是的,重新排序将是最后一部分。我现在正在尝试使用 ldrb 指令,但它似乎无法从 .quad 值加载一个字节。我在汇编程序中这样定义我的查找表: .global t0 t0: .quad 0xc6a597f4a5f432c6 .quad 0xf884eb9784976ff8 但 ldrb r1, t0 失败。为什么我应该使用“vld1.64”而不是“vldr.64”?
  • 这可能是进行实际查找的唯一方法,因为查找的值是 64 位的。但是,可以通过在四寄存器上执行它们来减少 veor 操作的数量。将四个查找到的值加载到 d0、d1、d2 和 d3 中。对 q0, q0, q1 进行 veor,然后对 d0, d0, d1 进行 veor,您将在两次操作中完成四个值的异或。
  • 使用 ldrb 它现在比 C 版本快一点。你有什么关于重新排序代码以提高性能和/或 AR/NEON 流水线的技巧吗?
  • 谢谢 - 它看起来很有帮助。这也适用于 Cortex-A9 吗?或许你也可以考虑提供英文版?
【解决方案4】:

尝试使用 NEON“内在函数”。基本上它们是编译成 NEON 指令的 C 函数。编译器仍然可以完成所有的指令调度,而你可以免费获得其他无聊的东西(移动数据)。

它并不总是完美地工作,但它可能比尝试手动编码更好。

寻找arm_neon.h

【讨论】:

    猜你喜欢
    • 2012-07-15
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多