nihui · 2022年07月12日

ncnn在树莓派1代dsp优化和超频实验

ncnn在树莓派1代dsp优化和超频实验

可在不修改本文章内容和banner图前提下,转载本文

电子垃圾迎来了春天

前些日子,去海鲜市场搞来个树莓派1b+,SOC BCM2835,700MHz ARM1176JZF-S处理器,内存512M,无WiFi

树莓派1的cpu是armv6架构,不具备neon指令集,但具备dsp增强指令集

关于 arm dsp 增强指令集

早在 ARMv5TE 和 ARMv5TEJ 架构已经加入了 dsp 增强指令,有 ARM926EJ-S 实现

在 armv6 中进一步扩充了一些 8bit dsp 指令,形成更系统的 dsp 指令集

在 Cortex-M4、Cortex-M7、Cortex-M33、Cortex-M35P、Cortex-M55 等 arm 低功耗系列中也加入了 dsp 指令集,指令基本与 armv6 保持一致

arm dsp 中的关键指令 smlad 能实现 i32 += i16 * i16 + i16 * i16,适合量化神经网络中 sgemm/winograd 做加速

gcc/clang 有宏 __ARM_FEATURE_SIMD32 判定是否编译支持 dsp 指令

gcc-10.1 及更新版本支持 arm dsp intrinsics,头文件为 arm_acle.h

需要使用 inline assembly,以便兼容更老的编译器。由于 dsp 指令的操作数是整数寄存器,在debug模式编译,-fno-omit-frame-pointer 结合 -fpic 后可自由使用的寄存器不足10个,导致错误error: ‘asm’ operand has impossible constraints,需要针对此情况写成分开的inline assembly语句。相较于龙芯 loongson-mmi 使用64bit浮点寄存器,arm dsp不够灵活,寄存器压力也较大。

下面代码引用自 ncnn/src/layer/arm/convolution_winograd_dot_int8.h

#if __ARM_FEATURE_SIMD32
for (; j + 1 < inch; j += 2)
{
    // fomit-frame-pointer implied in optimized flag spare one register
    // let us stay away from error: ‘asm’ operand has impossible constraints   --- nihui
#if __OPTIMIZE__
    asm volatile(
        "ldr    r2, [%0], #4    \n" // int16x2_t _val02 = *((int16x2_t*)r0); r0 += 2;
        "ldr    r3, [%0], #4    \n" // int16x2_t _val13 = *((int16x2_t*)r0); r0 += 2;
        "ldr    r4, [%1], #4    \n" // int16x2_t _w02 = *((int16x2_t*)k0); k0 += 2;
        "ldr    r5, [%1], #4    \n" // int16x2_t _w13 = *((int16x2_t*)k0); k0 += 2;
        "smlad  %2, r2, r4, %2  \n" // sum00 = __smlad(_val02, _w02, sum00);
        "smlad  %3, r3, r4, %3  \n" // sum01 = __smlad(_val13, _w02, sum01);
        "smlad  %4, r2, r5, %4  \n" // sum10 = __smlad(_val02, _w13, sum10);
        "smlad  %5, r3, r5, %5  \n" // sum11 = __smlad(_val13, _w13, sum11);
        : "=r"(r0),
        "=r"(k0),
        "=r"(sum00),
        "=r"(sum01),
        "=r"(sum10),
        "=r"(sum11)
        : "0"(r0),
        "1"(k0),
        "2"(sum00),
        "3"(sum01),
        "4"(sum10),
        "5"(sum11)
        : "memory", "r2", "r3", "r4", "r5");
#else
    int _val02 = *((int*)r0);
    int _val13 = *((int*)(r0 + 2));
    int _w02 = *((int*)k0);
    int _w13 = *((int*)(k0 + 2));
    asm volatile("smlad %0, %2, %3, %0"
                    : "=r"(sum00)
                    : "0"(sum00), "r"(_val02), "r"(_w02)
                    :);
    asm volatile("smlad %0, %2, %3, %0"
                    : "=r"(sum01)
                    : "0"(sum01), "r"(_val13), "r"(_w02)
                    :);
    asm volatile("smlad %0, %2, %3, %0"
                    : "=r"(sum10)
                    : "0"(sum10), "r"(_val02), "r"(_w13)
                    :);
    asm volatile("smlad %0, %2, %3, %0"
                    : "=r"(sum11)
                    : "0"(sum11), "r"(_val13), "r"(_w13)
                    :);
    r0 += 4;
    k0 += 4;
#endif
}
#endif // __ARM_FEATURE_SIMD32

ncnn 基于 arm dsp 的优化效果

对比 arm dsp 优化效果,可看出性能提升很有限,这主要是因为 arm dsp 是 32bit simd,本身的运算能力不高(arm neon是128bit simd),并且要占用宝贵的整数寄存器资源,代码循环不能更激进的展开,也不能奢侈把数据放在寄存器中缓存

image.png

树莓派1b+ cpu 超频实验

运行sudo raspi-config,选择4 Performance Options,选择P1 Overclock,可配置超频

树莓派1代的cpu本身比较凉快,即使超频也依旧凉快。超频后 ncnn 推理速度更快,很棒棒!

1000MHz经常死机,故没有成绩

image.png

总结

据说目前市场上能容易获得的最强 armv6 是三星 S3C6410,但是软件生态上树莓派实在是太优秀 qwq

帮armv6优化写完后,惊喜的发现Cortex-M系列也有同样的指令集,以后可以找个Cortex-M的SOC试试效果 :)

作者:nihui
文章来源:知乎

推荐阅读

更多嵌入式AI相关技术干货请关注嵌入式AI专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
18803
内容数
1348
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息