【问题标题】:What's the correct ARMv7 to ARMv8 NEON port of this matrix multiplication code?这个矩阵乘法代码的正确 ARMv7 到 ARMv8 NEON 端口是什么?
【发布时间】:2019-03-08 16:39:24
【问题描述】:
    // http://infocenter.arm.com/help/topic/com.arm.doc.dai0425/DAI0425_migrating_an_application_from_ARMv5_to_ARMv7_AR.pdf
// p. 4-21

.macro mul_col_f32 res_q, col0_d, col1_d
vmul.f32 \res_q, q8, \col0_d[0] @ multiply col element 0 by matrix col 0
vmla.f32 \res_q, q9, \col0_d[1] @ multiply-acc col element 1 by matrix col 1
vmla.f32 \res_q, q10, \col1_d[0] @ multiply-acc col element 2 by matrix col 2
vmla.f32 \res_q, q11, \col1_d[1] @ multiply-acc col element 3 by matrix col 3
.endm

// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100748_0606_00_en/lmi1470147220260.html
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203j/Cacjfjei.html

.globl  mat44mulneon
.p2align 2 // what's this ?
.type mat44mulneon,%function
mat44mulneon:
.fnstart // not recognized by eclipse syntax coloring?
// ---------
vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0
vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0
vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1.
vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1.
mul_col_f32 q12, d0, d1 @ matrix 0 * matrix 1 col 0
mul_col_f32 q13, d2, d3 @ matrix 0 * matrix 1 col 1
mul_col_f32 q14, d4, d5 @ matrix 0 * matrix 1 col 2
mul_col_f32 q15, d6, d7 @ matrix 0 * matrix 1 col 3
vst1.32 {d24-d27}, [r0]! @ store first eight elements of result.
vst1.32 {d28-d31}, [r0]! @ store second eight elements of result.
// ---------
bx lr // Return by branching to the address in the link register.
.fnend

我在 ARM 网站上找到的上述代码(参见 cmets 中的链接)适用于我的 ARM Cortex A9 机器,即 ARMv7 机器。

我现在正试图让它在 ARMv8 / aarch64 CPU 上运行。 我找到了这张幻灯片: porting to ARM64

最后,它显示了一个矩阵乘法代码。但它使用循环,我猜(如果我看不正确,请纠正我)如果移植到新的 ARMv8 助记符,我发布的代码会更快。 链接的文档还显示了一些 v7 -> v8 更改,例如我将 vmul.32 之类的内容更改为 fmul 等等。示例中给出的寄存器名称与上面发布的代码中的名称不匹配。由于我对任何 ARM asm 都不完全流利,所以我不知道这里有什么等价物。 例如。当我构建我的项目时,我收到如下错误:

operand 1 must be a SIMD vector register list -- `st1 {d24-d27},[r0]

不过,我不确定这是否是唯一的问题,所以我宁愿问: 代码需要做哪些改动才能在aarch64机器上运行?

【问题讨论】:

  • 不知道你的问题的答案,但你为什么要使用汇编?这是可能实现您想要的 C++ 代码:github.com/Microsoft/DirectXMath/blob/master/Inc/…
  • @Soonts 谢谢,我会试试的。我在某处读到 SIMD 内在函数不能做汇编程序可以做的一些事情(是否是多字加载......),因此使用(正确的)汇编程序代码会更快。但也许这已经改变了,或者是/仅适用于除 NEON 之外的其他 SIMD - 唉,我不记得细节了。
  • 确实,有几个极端情况。但根据我的经验,很难击败编写良好的内在代码。编译器似乎知道指令延迟并相应地重新排序它们。 C++ 更容易编写,因为自动寄存器分配、类型安全、优化器中的常量传播。它们在架构中可移植,即从 x86 到 AMD64,从 ARMv7 到 v8。
  • @Soonts:上次我听说(大约 6 个月前),ARM 的 gcc/clang 在优化内在函数(以及周围的标量代码)方面仍然非常糟糕。例如额外的存储/重新加载、冗余的寄存器复制等是我在查看 asm 输出时记得的那种事情。这很奇怪,因为它们可以使用 x86 SSE/AVX 和 PowerPC Altivec。但除非这种情况发生变化,否则在 ARM 上仍然有专门用于手写 asm 的用例。
  • @Sonts 我的低密度奇偶校验的汇编版本的速度是我自己编写的内在函数版本的 3 倍以上。我不会称那一对边。

标签: assembly simd neon armv8


【解决方案1】:

这是该例程的粗略 AArch64 版本:

.macro mul_col_f32 res, col
    fmul \res, v16.4s, \col[0] // multiply col element 0 by matrix col 0
    fmla \res, v17.4s, \col[1] // multiply-acc col element 1 by matrix col 1
    fmla \res, v18.4s, \col[2] // multiply-acc col element 2 by matrix col 2
    fmla \res, v19.4s, \col[3] // multiply-acc col element 3 by matrix col 3
.endm

.globl  mat44mulneon
mat44mulneon:
    ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [x1] 
    ld1 {v0.4s,  v1.4s,  v2.4s,  v3.4s},  [x2] 
    mul_col_f32 v24.4s, v0.s // matrix 0 * matrix 1 col 0
    mul_col_f32 v25.4s, v1.s // matrix 0 * matrix 1 col 1
    mul_col_f32 v26.4s, v2.s // matrix 0 * matrix 1 col 2
    mul_col_f32 v27.4s, v3.s // matrix 0 * matrix 1 col 3
    st1 {v24.4s, v25.4s, v26.4s, v27.4s}, [x0] 
ret

除了链接演示文稿中提到的一般事项外,还有一些关于转换的非全面说明:

  • 在 AArch32 中,一条 ld1 指令最多可以加载 64 个字节,而在 AArch32 中,vld1 最多可以加载 32 个字节。这避免了增加 r0/r1/r2 或 x0/x1/x2 指针的需要
  • 我省略了特定于操作系统/二进制格式的.fnstart.fnend.type,如果需要,可以在与原始版本相同的位置读取它们
  • 对于 AArch64 程序集,@ 不再是注释字符
  • mul_col_f32col 参数格式为v0.s,与v0.4s 相反。选择特定车道时,在与宏中的[0] 后缀连接后,应省略车道数,例如要选择v0.4s 寄存器的第一个通道,它应该写为v0.s[0]。 GNU 汇编器确实允许 v0.4s[0],但其他汇编器(包括 Clang/LLVM 内置汇编器和 Microsoft 的 armasm64)只允许前一种语法。

【讨论】:

  • 谢谢!根据记录,此版本在 Cortex A72 上比链接移植演示中的版本快 5%。
猜你喜欢
  • 2021-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 2011-02-21
  • 2014-08-28
  • 2018-05-08
相关资源
最近更新 更多