ARM NEON 编程系列1 - 导论

2020-01-13来源: eefocus关键字:ARM  NEON  编程系列

前言

本系列博文用于介绍ARM CPU下NEON指令优化。


博文github地址:github

相关代码github地址:github

NEON历史

ARM处理器的历史可以阅读文献[2],本文假设读者已有基本的ARM CPU下编程的经验,本文面向需要了解ARM平台下通过NEON进行算法优化的场景。


ARM CPU最开始只有普通的寄存器,可以进行基本数据类型的基本运算。自ARMv5开始引入了VFP(Vector Floating Point)指令,该指令用于向量化加速浮点运算。自ARMv7开始正式引入NEON指令,NEON性能远超VFP,因此VFP指令被废弃。


NEON用途

类似于Intel CPU下的MMX/SSE/AVX/FMA指令,ARM CPU的NEON指令同样是通过向量化计算来进行速度优化,通常应用于图像处理、音视频处理等等需要大量计算的场景。


Hello world

下面给一个最基本的例子来说明NEON的作用:

注意:


代码采用C++11编写,后续博客代码均以C++11编写,不再重述)

此系列博客采用neon2sse.h将NEON指令翻译成SSE指令以使得代码可以在x86/x64 CPU上运行。本文所有代码均在windows vs2013以及android-ndk-r11c下编译测试通过。


完整代码地址:基本NEON优化示例代码

//填充随机数

static void fill_random_value(std::vector& vec_data)

{

    std::uniform_real_distribution distribution(

        std::numeric_limits::min(),

        std::numeric_limits::max());

    std::default_random_engine generator;


    std::generate(vec_data.begin(), vec_data.end(), [&]() { return distribution(generator); });

}

//判断两个vector是否相等

static bool is_equals_vector(const std::vector& vec_a,

  const std::vector& vec_b)

{

    if (vec_a.size() != vec_b.size())

    {

        return false;

    }

    for (size_t i = 0; i < vec_a.size(); i++)

    {

        if (vec_a[i] != vec_b[i])

        {

            return false;

        }

    }

    return true;

}

//正常的vector相乘 (注意:需要关闭编译器的自动向量化优化)

static void normal_vector_mul(const std::vector& vec_a,

  const std::vector& vec_b,

  std::vector& vec_result)

{

    assert(vec_a.size() == vec_b.size());

    assert(vec_a.size() == vec_result.size());

    //compiler may optimized auto tree vectorize (test this diabled -ftree-vectorize)

    for (size_t i = 0; i < vec_result.size();i++)

    {

        vec_result[i] = vec_a[i] * vec_b[i];

    }

}

//NRON优化的vector相乘

static void neon_vector_mul(const std::vector& vec_a,

  const std::vector& vec_b,

  std::vector& vec_result)

{

    assert(vec_a.size() == vec_b.size());

    assert(vec_a.size() == vec_result.size());

    int i = 0;

    //neon process

    for (; i < (int)vec_result.size() - 3 ; i+=4)

    {

        const auto data_a = vld1q_f32(&vec_a[i]);

        const auto data_b = vld1q_f32(&vec_b[i]);

        float* dst_ptr = &vec_result[i];

        const auto data_res = vmulq_f32(data_a, data_b);

        vst1q_f32(dst_ptr, data_res);

    }

    //normal process

    for (; i < (int)vec_result.size(); i++)

    {

        vec_result[i] = vec_a[i] * vec_b[i];

    }

}

//测试函数

//FuncCostTimeHelper是一个计算时间消耗的helper类

static int test_neon()

{

    const int test_round = 1000;

    const int data_len = 10000;

    std::vector vec_a(data_len);

    std::vector vec_b(data_len);

    std::vector vec_result(data_len);

    std::vector vec_result2(data_len);

    //fill random value in vecA & vecB

    fill_random_value(vec_a);

    fill_random_value(vec_b);

    //check the result is same

    {

        normal_vector_mul(vec_a, vec_b, vec_result);

        neon_vector_mul(vec_a, vec_b, vec_result2);

        if (!is_equals_vector(vec_result,vec_result2))

        {

            std::cerr << "result vector is not equals!" << std::endl;

            return -1;

        }

    }

    //test normal_vector_mul

    {

        FuncCostTimeHelper time_helper("normal_vector_mul");

        for (int i = 0; i < test_round;i++)

        {

            normal_vector_mul(vec_a, vec_b, vec_result);

        }

    }

    //test neon_vector_mul

    {

        FuncCostTimeHelper time_helper("neon_vector_mul");

        for (int i = 0; i < test_round; i++)

        {

            neon_vector_mul(vec_a, vec_b, vec_result2);

        }

    }

    return 0;

}


int main(int, char*[])

{

    return test_neon();

}

说明:


这段代码在关闭编译器的自动向量化优化之后,neon_vector_mul大约比normal_vector_mul速度快3倍左右。

这段代码中使用了3条NEON指令:vld1q_f32,vmulq_f32,vst1q_f32。具体指令的作用会在后续博文中说明。

关键字:ARM  NEON  编程系列 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic485489.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:ARM S3C2410学习手记
下一篇:ARM NEON 编程系列2 - 基本指令集

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

ARM linux内核在内存中的布局
Kernel Memory Layout on ARM Linux Russell King <rmk@arm.linux.org.uk>      November 17, 2005 (2.6.15)This document describes the virtual memory layout which the Linuxkernel uses for ARM processors.  It indicates which regions arefree for platforms to use, and which are used by generic
发表于 2020-01-19
ARM命令LDREX和STREX实现spinlock
在 include/asm-arm/spinlock.h 下有這麼一段#if __LINUX_ARM_ARCH__ < 6#error SMP not supported on pre-ARMv6 CPUs#endif好啦,前提就是:只有 ARM core 版本 >=6 才可以繼續:all spin lock primitives 到最後都是使用下面這個基本型: static inline void __raw_spin_lock(raw_spinlock_t *lock){    unsigned long tmp;1 
发表于 2020-01-19
ARM用户层发生异常后软硬件协同处理流程
我这里是要简单说一下,在ARM平台的用户层发生异常后的软硬件协同处理流程,是个大致的概况,对宏观了解后,具体细节内容网上有很多,可以自行查询。用户层程序正在执行时,遇到未定义的指令(ARM不是别的指令)或者SWI软件中断指令(产生系统调用),就会产生异常,这里以未定义指令异常为例进行说明:一旦出现未定义指令异常,CPU会自动做如下操作:(1)未定义模式(ARM七种运行模式的一种)下对应的lr(即R14,不同的运行模式有不同的lr寄存器)寄存器保存当前发生异常的指令下一条指令的地址。例如,在用户态有A B C 三条指令顺序执行,指令A发生未定义指令异常,则指令B的地址就会由CPU保存到未定义模式下的lr寄存器中,用于异常返回
发表于 2020-01-19
ARM处理器各个模式之间是如何切换的?
1、ARM处理器各个模式之间是如何切换的?答:除用户模式外的其他6种模式称为特权模式,这些模式中,程序可以访问所有系统资源,也可以任意进行处理器模式的切换。处理器模式可以通过软件控制进行切换(直接设置CPSR寄存器的后五位就可以在6种特权模式之间互相切换),也可以通过外部中断或异常处理过程进行切换(例如,在USR模式下,发生中断后切换到IRQ模式)。2、ARM各个模式之间切换时,上下文的保存哪些是硬件在做?哪些是操作系统在做?答:CPU做的:(1)把返回地址保存到相应模式的lr寄存器中,例如从usr模式切换到irq模式,CPU会将usr模式下的pc值,保存到irq模式下的lr寄存器中。(2)保存CPSR到相应模式的SPSR寄存器中
发表于 2020-01-19
ARM处理器的运行模式和ARM寄存器
一、ARM处理器共有7种运行模式 处理器模式描述用户模式(User,usr)正常程序执行的模式快速中断模式(FIQ,fiq)用于高速数据传输和通道处理外部中断模式(IRQ,irq)用于通常的中断处理特权模式(Supervisor,sve)供操作系统使用的一种保护模式数据访问中止模式(Abort,abt)用于虚拟存储及存储保护未定义指令中止模式(Undefined,und)用于支持通过软件仿真硬件的协处理器系统模式(System,sys)用于运行特权级的操作系统任务usr是普通模式,其他六种是特权模式(Privileged Modes),在这些模式下,程序可以访问所有的系统资源,也可以任意地进行处理器模式的切换。除了usr
发表于 2020-01-18
ARM处理器的运行模式和ARM寄存器
ARM裸机驱动中的main函数调用前的准备工作
硬件方面1.关闭CPU看门狗2 配置CPU的工作时钟3.程序要在SDRAM中运行,因此必须初始化SDRAM软件方面1 函数要运行,需要栈空间,因此必须初始化栈指针SP2 设置main函数的返回地址3 调用main4 清理工作
发表于 2020-01-18
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved