我有一个音乐合成应用程序,在基于Yocto的RTLinux下以32位模式运行在RPi3(Cortex-A53)上。我使用GCC 6.3来编译代码,该代码在C ++中使用大量的SIMD内在函数来操作float32x4_t和int32x4_t数据。代码经过检测,以便我可以看到执行某些相当大的SIMD块需要多长时间。它运作良好,直到几天前,在摆弄不相关的东西后,它突然放慢了两倍以上。
我进去看了一下正在生成的代码。在过去,代码看起来很漂亮,效率很高。现在,它甚至在大多数地方都没有使用SIMD。我检查了编译器选项。它们包括-marm -mcpu=cortex-a53 -mfloat-abi=hard -mfpu=crypto-neon-fp-armv8 -O3
。有时您会在生成的代码中看到q寄存器,因此它知道它们存在,但大多数情况下它在s寄存器上运行。此外,它使用大量代码将q8-q15(a.k.a. d16-d31)的各个部分移动到通用寄存器中,然后返回到s0-s31寄存器以对它们进行操作,然后将它们移回,这非常低效。有没有人知道为什么编译器应该突然开始将float32x4_t和int32x4_t向量内部函数编译成单个标量操作?或者通过让编译器咳出一些关于内部发生的事情的信息来诊断这个问题?
编辑:我发现在某些地方我在int32x4_t和float32x4_t类型上做直接算术,而在其他地方我使用的是ARM内部函数。在后一种情况下,我得到了SIMD指令,但在前者中它使用了标量。当我使用所有内在函数重写代码时,SIMD指令重新出现,执行时间下降到接近之前的水平。但是我注意到如果我写了像x += y * z;
这样的东西,编译器会使用标量但是足够智能使用四个VFMA指令,而如果我写x = vaddq_f32(x, vmulq_f32(y, z));
它会使用VADDQ和VMULQ指令。这就解释了为什么它在将算术运算符编译成SIMD时并不像以前那么快。
现在的问题是:为什么编译器愿意在int32x4_t和float32x4_t值上编译直接算术之前,而不是更多?有没有一些模糊的选择,我没有意识到我在那里,现在我失踪了?