对于具有单独流水线的ARM处理器,指定“-mfpu = neon-vfpv3”是否比指定“-mfpu = neon”有优势?

7

我的Zynq-7000 ARM Cortex-A9处理器同时具备NEON和VFPv3扩展功能,而且Zynq-7000-TRM说明该处理器配置为具有“独立的VFPv3和高级SIMD指令管道”。

到目前为止,我使用Linaro GCC 6.3-2017.05编译程序,并使用-mfpu=neon选项来利用SIMD指令。但是,在编译器还需要发出非SIMD操作的情况下,使用-mfpu=neon-vfpv3会有所不同吗?GCC的指令选择和调度器是否会生成两个版本的指令,以便可以同时利用两个管道,从而提高CPU的利用率?


1
你还需要使用-ffast-math来进行自动向量化,因为ARMv7 NEON 不支持denormal(非规格化数),因此不完全符合IEEE标准。(或类似的原因;你需要使用-ffast-math来进行自动向量化,但是你可以在不使用-ffast-math的情况下使用NEON指令)。 - Peter Cordes
@PeterCordes 是的,即使使用了-funsafe-math-optimizations也可以进行向量化。但我想知道对于非向量化代码或同时包含向量化和非向量化变体的代码,是否可以通过利用两个流水线来提高性能。 - Johannes Schaub - litb
2
我只了解x86 CPU的这种微架构细节,不了解ARM。所以问得好,这就是为什么我点赞的原因。希望有人能回答。这与x86“-march=sse+387”略有相似,但它们使用不同的架构寄存器。VFP和NEON使用相同的寄存器,因此有更多的希望进行良好的代码生成。 - Peter Cordes
@PeterCordes,这个“需要-ffast-math来进行自动向量化,因为ARMv7 NEON不支持非规格化数,因此不完全符合IEEE标准”的说法今天是否仍然适用?armv8 64位也是如此吗? - Danijel
@Danijel:如果我没记错的话,AArch64 SIMD支持denomrals也就是subnormals。我忘了ARMv7是否有一个控制位来实现这一点,但可能ARMv8在32位模式下具有符合IEEE标准的SIMD。 - Peter Cordes
好的,-ffast-math 对于 armv8 不是必需的。 - Danijel
3个回答

4

从技术上讲,是的。

但实际情况并非如此。

在ARMv7上,NEON是可选的。

授权方可以从以下配置中选择一个:

  • 仅VFP
  • NEON加VFP

与NEON不同,在ARMv7上有不同的VFP版本,其中Cortex-A8上的VFP-lite因为不能流水线处理而变得非常缓慢。

因此,从技术上讲,通过编译器选项指定CPU配置和架构版本是有意义的,这样编译器就可以为特定的架构/配置生成最优化的机器代码。

然而,在现实中,这些编译选项和指令大多被编译器忽略了。

VFP和NEON指令分配到不同的管道并没有太大帮助,因为它们都共享寄存器库。

通过尽可能利用多个寄存器来提高NEON的性能比让VFP并行运行要有更大的收益。

我不明白为什么现在有这么多人对免费编译器如此信任。

最好的ARM编译器无疑是ARM的DS-5 Ultimate Edition,售价超过6000美元。他们的支持非常好,但我不确定这是否值得这个价格。


根据此处所述,Cortex-A9是“部分乱序”的。那是否意味着它需要其所有的架构寄存器来进行NEON指令的软件流水线处理?因为如果不是这样,那么根据问题的不同,可能还有一些空余带宽(和物理寄存器文件空间)可以混合一些VFP,如果该CPU上的VFP不是太糟糕的话。不过这样做是否有意义呢?(虽然我认为gcc无法很好地处理这个问题,但我更感兴趣的是优化汇编代码是否能够利用这一点)。 - Peter Cordes
@PeterCordes 我认为A9不会按任意顺序执行NEON指令,鉴于我自己进行的测试结果。即使是Galaxy-S7上的Exynos 8890似乎也没有有效地利用乱序引擎。我很快就会在我的博客上发布这些测试,并且你将是第一个被通知的人。那将是一个神话的破解。 - Jake 'Alquimista' LEE
它至少进行寄存器重命名以便可以重复使用寄存器而不会产生错误依赖关系吗?(即避免WAW和WAR危险)我猜这可能是他们所说的“部分乱序”的意思。有重命名的顺序(Tomasulo算法)不需要昂贵的调度程序,只需要在流水线中有一个RAT(寄存器分配表)和一个重命名阶段。(还有更简单但功能更弱的重命名方案,如记分牌)。 - Peter Cordes
在整数核心上,很可能是的,但从我到目前为止所看到的情况来看,我甚至怀疑大集群NEON是否按顺序执行,至少在特定的三星定制设计上是这样。 - Jake 'Alquimista' LEE
哦,我之前的评论没有想得太清楚。我认为,顺序执行并不能从寄存器重命名中获得足够的好处来证明它是有价值的。如果指令按顺序开始,即使某些指令具有更高的延迟,它们仍然可以进行乱序写回,因此WAW是可能存在的危险,但不是WAR。(我认为我们可以假设每个指令在下一个指令写回之前读取其操作数)。单独的WAW不太可能发生:大多数代码不会写入它们从未读取的寄存器,并且顺序执行会防止写入在等待其输入的指令之前开始。这就是所有寄存器重命名所能做到的。 - Peter Cordes

3

ARM的Cortex-A9 NEON/VFP手册(Cortex™-A9 NEON™媒体处理引擎)在第3.2节写最佳VFP和高级SIMD代码中指出:

以下准则可以提供VFP和高级SIMD代码的显着性能提升:尽可能避免

  • ...

  • 混合只有Advanced SIMD指令和只有VFP指令。

它说它可以与ARM或Thumb指令(即标量整数代码)并行执行NEON和VFP指令,“除了同时加载和存储”。

不完全清楚他们是否意味着完全避免同时执行它们,还是避免VFP和NEON指令之间存在数据依赖关系。容易想象后者对于前者不适用的原因会很糟糕(例如,可能不存在在不同域中的执行单元之间的旁路转发)。


同一文档中的循环时间表明,与NEON指令相比(即使延迟时间相同),VFP标量指令在流水线中需要更长的时间,因此对于无法进行向量化的代码,使用VFP可能是一个优势,即使使用了-ffast-math。或者如果我理解正确,NEON具有较低的延迟MUL,因此可能对于长依赖链来说是一个优势。
Cortex-A9如果具有VFP,则具有完全流水线化的VFP浮点运算器,例如:
  • VADD/VSUB .F (Sn)或.D(Dn)(VFP):1个周期的吞吐量。需要在第一个周期输入,结果在第四个周期准备好。(所以延迟4个周期?)

  • VADD/VSUB Dn(NEON):1个周期的吞吐量。需要在第二个周期输入,结果在第五个周期准备好(第六个周期写回)。(所以延迟4个或5个周期? 取决于什么消耗了结果)。

  • VADD/VSUB Qn(NEON):(每个)2个周期的吞吐量。需要在第二个和第三个周期输入,结果在第五个和第六个周期准备好。(写回比这晚1个周期)(所以延迟4个或5个周期?)。

  • VMUL .F Sd,Sn,Sm(VFP):1个周期的吞吐量,需要在第一个周期输入,结果在第五个周期准备好。(所以延迟5个周期?)

  • VMUL(VFP)的双精度没有列出,只有VNMUL(2个周期的吞吐量)。

  • VMUL(NEON):与VADD/VSUB相同的时间。也许不处理非规格化数可以节省时间?如果我读对了,它实际上比VFP的延迟更低,除了指令需要更早地发出。

还有针对乘加的特殊结果转发。请参阅PDF文档。


1
vmulvmla/vmls指令似乎存在某种切换开销。几周前有人问过这个问题。我自己进行了一些测试,看起来是这样的。不过我从未在任何文档中读到过类似的内容。 - Jake 'Alquimista' LEE

3
答案将取决于gcc的版本,这可能会在未来发生变化。在中的当前代码将NEON/VFP描述为一个组合单元。该行代码是:
(define_cpu_unit "ca9_issue_vfp_neon, cortex_a9_ls" "cortex_a9")

带有注释的代码:

;; The Cortex-A9 core is modelled as a dual issue pipeline that has
;; the following components.
;; 1. 1 Load Store Pipeline.
;; 2. P0 / main pipeline for data processing instructions.
;; 3. P1 / Dual pipeline for Data processing instructions.
;; 4. MAC pipeline for multiply as well as multiply
;;    and accumulate instructions.
;; 5. 1 VFP and an optional Neon unit.
;; The Load/Store, VFP and Neon issue pipeline are multiplexed.
;; The P0 / main pipeline and M1 stage of the MAC pipeline are
;;   multiplexed.
;; The P1 / dual pipeline and M2 stage of the MAC pipeline are
;;   multiplexed.
;; There are only 4 integer register read ports and hence at any point of
;; time we can't have issue down the E1 and the E2 ports unless
;; of course there are bypass paths that get exercised.
;; Both P0 and P1 have 2 stages E1 and E2.
;; Data processing instructions issue to E1 or E2 depending on
;; whether they have an early shift or not.

ca9_issue_vfp_neon单元用于描述NEON和VFP指令。因此,调度程序在计算成本时不会知道这些指令可以进行流水线处理。但是,它可能会同时生成这两种指令,如果幸运的话,它们就会被流水线处理。

在“arm.c”中,有许多实例使用NEON来传输数据。如果您的代码具有许多结构的浮点数,编译器可能会混合使用NEON和VFP代码,其中NEON用于移动数据。

exynos这样的机器具有一些自定义调整,例如使用NEON进行字符串操作,而您的Zync CPU将无法获得该调整描述在arm.c中。

另外,如果您没有指定-mfpu = neon-vfpv3,则任何带有“vfpv3”指令的内联汇编都将无效。


根据GCC版本的不同,情况会有所改变。但是,您可以在“cortex-a9.md”中查找CPU描述,以查看编译器是否可能以不同的方式安排指令。此外,“arm.c”文件执行指令成本计算;如果NEON成本未在其中实现,则编译器将永远不会生成指令。

即使这样做是可行的,在处理更简单的ARMv5 DSP指令时也会遇到困难,您会发现只有1-2%的指令会发生变化。在多兆字节的图像中,这样的选项只会改变几百个操作码,原因是其他人已经给出了共享寄存器、“C”浮点语义等(的限制)。

但是,如果-mfpu = neon-vfpv3确实描述了您的CPU,为什么不将其用于嵌入式应用程序呢?通用选项旨在生成可以在多种设备上运行的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接