7

我正在使用 ARM NEON 内在函数(llvm,iOS)对内部循环进行矢量化。我一般用float32x4_ts。我的计算需要对这个向量中四个浮点数中的三个求和。

此时我可以退回到 C 浮点数,然后vst1q_f32取出四个值并将我需要的三个值相加。但我认为,如果有一种方法可以直接用一两条指令中的向量来完成它,然后只获取一个通道结果,那么它可能会更有效,但我想不出任何明确的路径来做到这一点。

我是 NEON 编程的新手,现有的“文档”非常可怕。有任何想法吗?谢谢!

4

3 回答 3

5

您应该能够使用 VFP 单元来完成此类任务。NEON 和 VFP 共享相同的寄存器库,这意味着您无需在寄存器周围进行洗牌即可利用一个单元,并且它们还可以对相同的寄存器位具有不同的视图。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204j/ch05s03s02.html

float32x4_t是 128 位的,因此它必须位于 Quad (Q) 寄存器上。如果您仅使用 arm 内在函数,您将不知道您使用的是哪一个。问题是如果它高于 4,VFP 不能将其视为单精度(对于好奇的读者:我保持简单,因为 VFP 版本之间存在差异,这是最低要求。)。因此,最好将您移动float32x4_t到固定寄存器,例如Q0. 在此之后,您可以将 S0、S1、S2 等寄存器与vadd.f32并将结果移回 ARM 寄存器。

一些警告... VFP 和 NEON 从理论上讲是不同的执行单元,它们共享相同的寄存器组和流水线。我不确定这种方法是否比其他方法更好,我不需要再说一遍,你应该做基准测试。此外,这种方法没有使用 neon 内在函数进行简化,因此您可能需要使用内联汇编来编写代码。

我做了一个简单的片段来看看它的样子,我想出了这个:

#include "arm_neon.h"

float32_t sum3() {           
        register float32x4_t v asm ("q0");
        float32_t ret;

        asm volatile(
        "vadd.f32       s0, s1\n"
        "vadd.f32       s0, s2\n"
        "vmov           %[ret], s0\n"
        : [ret] "=r" (ret)
        :
        :);

        return ret;
}

objdump它看起来像(用 gcc -O3 -mfpu=neon -mfloat-abi=softfp 编译)

00000000 <sum3>:
   0:   ee30 0a20   vadd.f32    s0, s0, s1
   4:   ee30 0a01   vadd.f32    s0, s0, s2
   8:   ee10 3a10   vmov    r0, s0
   c:   4770        bx  lr
   e:   bf00        nop

如果你试一试,我真的很想听听你的印象!

于 2012-12-14T09:17:15.517 回答
3

你能把第四个元素归零吗?也许只是通过复制和使用vset_lane_f32

如果是这样,您可以使用Sum all elements in a quadword vector in ARM assembly with NEON的答案,例如:

float32x2_t r = vadd_f32(vget_high_f32(input), vget_low_f32(input));
return vget_lane_f32(vpadd_f32(r, r), 0); // vpadd adds adjacent elements

虽然这实际上做的工作比你需要的要多,所以只提取三个浮点数vget_lane_f32并添加它们可能会更快。

于 2012-12-14T01:23:38.823 回答
2

听起来您想使用(某些版本的)VLD1 将零加载到您的额外通道中(除非您可以安排它已经为零),然后是两个 VPADDL 指令将四个通道成对相加成两个,然后是两个通道合而为一。

于 2012-12-14T01:22:56.970 回答