本篇随笔将简介如何使用C语言,去编写调用ARM NEON指令集的程序。
负优化警告,快跑

零、Neon指令集简介

和AVX等指令集类似,但Neon只能一口气算128位!!

一、引入头文件

首先你得引个头文件,接下来才能愉快玩耍:

#include <arm_neon.h>

二、数据结构定义

以下内容纯纯的是个人理解,没有任何资料做参考,因而也纯供参考,理性看待!

大部分数据结构都长这样:int8x8_t 或者 int64x2x4_t

根据文档,里头存的好像是存放数字的寄存器的标号。!!未经考证

如果把它分成三个部分,那就是——

<int/uint/float...><x1/x2/...>[x2/x3/x4/...]

第一部分<int/uint/float...>):向量里头每一个数字是啥类型的

第二部分<x1/x2/...>):这个x?,问号里头的数字,就是这个类型的变量所对应的寄存器中,存着几个某类型的值。
比如我们声明一个int8x8_t类型的变量vector_a,那么vector_a(对应的寄存器)里头,存着8个int类型的变量。 (注:vector_a里头存的好像是存放数字的寄存器的标号。!!未经考证)

第三部分([x2/x3/x4/...]):如果有第三部分,那么就意味着这是个“数组”,之所以加引号,是因为用的时候,是采取vec.val[0]这种方式调用的,本质是个结构体。x?,就意味着vec.val里头有几个元素;每个元素的类型,就是前面两个部分所定义的类型。
比如我们声明一个int64x2x4_t类型的变量vector_array,那么vector_array.val就是类型为int64x2_t类型的数组(对应int64x2x4_t中的int64x2),数组中有4个元素(对应int64x2x4_t中的x4),每个元素的类型是int64x2_t。具体要用哪个int64x2_t去处理,需要你使用vector_array.val[0/1/2/3]来指定

三、一些常用函数类

开始的开始,先给个官方查函数的网页,点这里进去,帮你勾选了只显示neon指令集的相关函数。

和avx类似,neon的指令,常用的不外乎:加载的、写回的、运算的

所谓加载,是把你正常数组中的数字,给干到专门用于算向量的寄存器里去
所谓写回,是把专门用于算向量的寄存器里头存的向量,给干到你的某个数组里头去
所谓运算,就是加减乘除移位啥的。

加载类

一般情况下用的加载类的函数长这样:vld1_s8,也可以拆成几个部分:

vld<1/2/3/4/..>[q]_[s/u/f/...][8/16/32/64]

一般只有一个参数,通常某个数组的地址就够用了。
根据具体函数,返回一个我们所提到的数据结构类型中的一种,比如int64x2x4_t

v:应该是代表这是指令;
ld:应该是load的缩写;
<1/2/3/4/..>:代表读几组。例如如果是vld【4】q_s64,返回得到的是一个类型为int64x2【x4】_t的东西。(不知我所云?看看上面数据结构定义那部分!)
[q],可有可无。没有,则代表就从你给的地址开始,读64位就行;有,那就读128位。例子我们在说完下面的东西后给。
_,一条朴实无华的下划线;
[s/u/f/...],代表每个元素的数据类型,s是有符号整数,u是无符号整数,f是浮点数等等;
[8/16/32/64],你这个数据类型有多少位。例如如果是vld4q_【s64】,那你每个元素就是一个64位的有符号整数。

所以,我们来举个具体例子——如果我们要从ptr这个地址读8个64位的无符号整数,读到专门用于计算的寄存器里头,该用啥函数呢?
答案是int64x2x4_t vector = vld4q_s64(ptr);,因为——

v ld 4 q _ s 64 ——
4,读4组数字;
q,一组读128位;
s,读的是无符号整数;
64,一个元素的大小是64位。

无符号整数,一个元素的大小是64位,将一组128位读入一个寄存器,那一组里头可以放2个64位的整数,而我们读4组,所以总共读8个64位的无符号整数。

那么返回啥类型的值呢?int64x2x4_t,64位的,一个寄存器里放俩64位的(int64x2x4_t中的x2),总共用了4个寄存器(int64x2x4_t中的x4)。

(未完待续)

标签: none

According to some rules, comments are banned. :)