NEON指令学习总结

Neon

定义:Neon指令是一个打包的SIMD架构。Neon的寄存器有16个128位四字寄存器Q0-Q15(armv8有32个Q1-Q31),32个64位双字寄存器D0-D31,两个寄存器是重叠的。两个寄存器的关系:Qn =D2n和D2n+1,如Q3是d6和d7的组合。
NEON寄存器
加载存储指令
vld和vst
在这里插入图片描述
在这里插入图片描述
交叉存取的示意图
在这里插入图片描述
NEON存储和加载的结构化方式
在这里插入图片描述
1.VLD或VST:加载或存储指令
2.interleave pattern:表示每个结构体元素间的间隔(1,2,3或4)
3.element type:表示每次访问单元的位宽比特数,即结构体内元素类型
4.NEON registers:读写的64-bit的NEON寄存器集合,最多可以列出4个寄存器,取决于interleave模式
5.r0:表示内存访问地址的ARM寄存器,该地址可以在每次访问时更新

加载(存储)模式
加载和存储指令可以用从1—4个相同大小的元素的交织结构体,这些元素可以是NEON指令通常支持的8,16或者32比特。
VLD1:从内存加载1~4个寄存器的数据,没有deinterleave,即线性加载;
VLD2:加载2或者4个寄存器的数据,解交织奇偶元素到各自的寄存器,这样很容易的把交织的立体声音频数据分解为左右声道的数据;
VLD3:加载3个寄存器的数据,很方便的把RGB的数据分为R、G、B通道;
VLD4:加载4个寄存器的数据,解交织,用于分解ARGB图像数据;
存储和加载类似,只是把寄存器的数据写到内存。

常用汇编指令

armv7 & armv8指令对比

arm-v8armv-7备注
加载数据(三通道)ld3 {v0.4s, v1.4s, v2.4s},[%[img]],#48①vld3.32 {d0,d2,d4}, [%[img]]!②vld3.32 {d1,d3,d5}, [%[img]]!交叉加载数据到三个128位寄存器,每个寄存器32X4
加载数据(单通道)ldp q0, q1, [%[rgb_b]], #32vld1.32 {q0-q1}, [%[rgb_b]]!连续加载数据到q0,q1寄存器,共8个32位数据
fadd v6.4s, v0.4s, %[constant2.0].4svadd.f32 q6, q0, %q[constant2.0]v6 = v0 + constant2.0
fsub v6.4s, v0.4s, %[constant1.0].4svsub.f32 q6, q0, %q[constant1.0]v6 = v0 - constant1.0
fmul v6.4s, v0.4s, %[constant0.5].4svmul.f32 q6,q0, %q[constant0.5]v6 = v0 * constant0.5
赋值(三通道)st3 {v9.8b-v11.8b} , [%[output]], #24vst3.u8 {d0, d1, d2}, [%[output]]!交叉从寄存器读取数据到output,每个寄存器8个8位数据,output交叉存入24个8位数据
赋值(单通道)st1 {v0.4s-v1.4s},[%[b]],#32vst1.f32 {q0, q1}, [%[b]]!连续从q0,q1寄存器读取数据存储到b
f32转u32fcvtau v0.4s, v6.4svcvt.u32.f32 q0, q6float32四舍五入转unsigned int32
u32转f32ucvtf v0.4s, v6.4svcvt.f32.u32 q0, q6unsigned int32转float32
32X4转16X8①SQXTUN v6.4h, v0.4s ② SQXTUN2 v6.8h, v1.4svqmovn.u32 d12, q0指令①v0赋值到v6的低位②v1赋值到v6的高位
16X8转8X8SQXTUN v9.8b, v6.8hvqmovn.u16 d0, q6v6对应h寄存器数据直接赋值给b寄存器低八位

intrinsics内置函数

代码实现

void neon_intrinsics_output(float *outputRes, unsigned char *resImgBuffer, int stride_len) {
        // 模型输出的nchw bgr数据
        float *output_ptr_b = outputRes;
        float *output_ptr_g = outputRes + stride_len;
        float *output_ptr_r = outputRes + stride_len * 2;
        // 转换成nhwc后的bgr数据
        uint8_t *dout_result_bgr = resImgBuffer;
        // 常数
        float32x4_t v_const_value_scale_mb0 = vdupq_n_f32(scale_mb[0]);
        float32x4_t v_const_value_scale_mb1 = vdupq_n_f32(scale_mb[1]);
        float32x4_t v_const_value_scale_mb2 = vdupq_n_f32(scale_mb[2]);

        float32x4_t v_const_value_mean_mb0 = vdupq_n_f32(mean_mb[0]);
        float32x4_t v_const_value_mean_mb1 = vdupq_n_f32(mean_mb[1]);
        float32x4_t v_const_value_mean_mb2 = vdupq_n_f32(mean_mb[2]);

        float32x4_t v_const_value_255_0 = vdupq_n_f32(255.f);

        int i = 0;
        for (; i < stride_len - 3; i += 4) { // 每次处理理4个像素
            // 顺序读取4个像素
            float32x4_t vin_b = vld1q_f32(output_ptr_b);
            float32x4_t vin_g = vld1q_f32(output_ptr_g);
            float32x4_t vin_r = vld1q_f32(output_ptr_r);
            // *
            float32x4_t vmul0 = vmulq_f32(vin_b, v_const_value_scale_mb0);
            float32x4_t vmul1 = vmulq_f32(vin_g, v_const_value_scale_mb1);
            float32x4_t vmul2 = vmulq_f32(vin_r, v_const_value_scale_mb2);
            // +
            float32x4_t vadd0 = vaddq_f32(vmul0, v_const_value_mean_mb0);
            float32x4_t vadd1 = vaddq_f32(vmul1, v_const_value_mean_mb1);
            float32x4_t vadd2 = vaddq_f32(vmul2, v_const_value_mean_mb2);
            // * 255
            float32x4_t vres0 = vmulq_f32(vadd0, v_const_value_255_0);
            float32x4_t vres1 = vmulq_f32(vadd1, v_const_value_255_0);
            float32x4_t vres2 = vmulq_f32(vadd2, v_const_value_255_0);
            // float32 to unsigned int32
            uint32x4_t vres0_int32 = vcvtq_u32_f32(vres0);
            uint32x4_t vres1_int32 = vcvtq_u32_f32(vres1);
            uint32x4_t vres2_int32 = vcvtq_u32_f32(vres2);
            // 移位
            uint16x4_t vres0_int16 = vqmovn_u32(vres0_int32);
            uint16x4_t vres1_int16 = vqmovn_u32(vres1_int32);
            uint16x4_t vres2_int16 = vqmovn_u32(vres2_int32);
            // 16x4 => 16x8 => 8x8
            uint8x8_t vout_8_0 = vqmovn_u16(vcombine_u16(vres0_int16, vres0_int16));
            uint8x8_t vout_8_1 = vqmovn_u16(vcombine_u16(vres1_int16, vres1_int16));
            uint8x8_t vout_8_2 = vqmovn_u16(vcombine_u16(vres2_int16, vres2_int16));
            // 模型输出结果指针
            uint8x8x3_t vout_bgr8 = {vout_8_0, vout_8_1, vout_8_2};
            // 指针移动4个像素
            output_ptr_b += 4;
            output_ptr_g += 4;
            output_ptr_r += 4;
            // unsigned char 结果
            vst3_u8(dout_result_bgr, vout_bgr8);
            dout_result_bgr += 12; // 4个像素 12个unsigned char
        }
        // 剩余元素(最多3个)一个一个像素处理
        for (; i < stride_len; i++) {
            *(dout_result_bgr++) = (*(output_ptr_b++) * scale_mb[0]) + mean_mb[0];
            *(dout_result_bgr++) = (*(output_ptr_g++) * scale_mb[1]) + mean_mb[1];
            *(dout_result_bgr++) = (*(output_ptr_r++) * scale_mb[2]) + mean_mb[2];
        }
    }

内置函数说明

intrinsics内置函数备注
加载数据(三通道)float32x4x3_t vin3 = vld3q_f32(dimg);交叉加载数据到三个寄存器,每个寄存器32X4
加载数据(单通道)float32x4_t vin_b = vld1q_f32(output_ptr_b);连续存储到一个寄存器
float32x4_t vadd0 = vaddq_f32(vin3.val[0], const_05);
float32x4_t vs0 = vsubq_f32(const_1, vres0_f32);
float32x4_t vmul0 = vmulq_f32(vin_b, v_const_value_scale_mb0);
赋值(三通道)vst3_u8(dout_result_bgr, vout_bgr8);
赋值(单通道)vst1q_f32(output_ptr_b, vs0);
f32转u32(就近原则)uint32x4_t vres0_int32 = vcvtq_u32_f32(vres0);
u32转f32float32x4_t vres0_f32 = vcvtq_f32_u32(vres0_int32);
32X4转16X4uint16x4_t vres0_int16 = vqmovn_u32(vres0_int32);
16X4转8X8uint8x8_t vout_8_0 = vqmovn_u16(vcombine_u16(vres0_int16, vres0_int16));
向量单个元素内存赋值vld1_lane_u8(uint8, uint8x8, index)uint8赋值给uint8x8,index是uint8的索引

参考:
https://blog.csdn.net/chshplp_liaoping/article/details/12752749
https://blog.csdn.net/panda1234lee/article/details/85158594

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值