性能优化篇(3):零基础快速入门 NEON

Author: stormQ

Created: Sunday, 24. November 2019 10:28PM

Last Modified: Wednesday, 28. October 2020 08:58PM



摘要

本文介绍了一些 NEON Intrinsics,涉及:向量赋值、访问向量、存储向量以及向量算术逻辑运算,并提供了如何编译和调试 NEON Intrinsics 的示例。

向量数据类型

向量类型:

<type><size><number_of_lanes>_t

向量数组:

<type><size><number_of_lanes><length_of_array>_t

注:type,表示数据类型,可选项:int(带符号整型)、uint(无符号整型)、float(浮点型)和 poly(多项式)。size,表示一个元素占用多少 bit。number_of_lanes,表示一个向量中包含的元素数量,第一个元素位于 lane[0] 的位置,第 n 个元素位于 lane[n-1] 的位置,lane[0] 对应向量寄存器最低位的size个 bit。length_of_array,向量数组的大小。

向量类型 含义
uint8x8_t 一个包含八个元素,元素类型为 8-bit 无符号整型的向量
uint8x8x2_t 一个包含两个向量的向量数组,每个向量包含八个元素,元素类型为 8-bit 无符号整型

向量赋值


向量常数赋值

返回上一级


uint8x8_t 向量常数赋值(每个元素的值都相同)

uint8x8_t vdup_n_u8(uint32_t a);
#include "arm_neon.h"

int main()
{
    // 声明一个包含八个元素的向量,每个元素的数据类型为 uint8_t
    uint8x8_t a_uint8x8;
    // 将向量的每个元素赋值为 255
    a_uint8x8 = vdup_n_u8(255);
    return 0;
}

注:使用NEON Intrinsics需要包含头文件arm_neon.h

$ g++ -std=c++11 -g -o assign_uint8x8_t assign_uint8x8_t.cpp
$ gdb -q ./assign_uint8x8_t
Reading symbols from ./assign_uint8x8_t...done.
(gdb) start
Temporary breakpoint 1 at 0x4005ac: file assign_uint8x8_t.cpp, line 8.
Starting program: /home/test/assign_uint8x8_t 

Temporary breakpoint 1, main () at assign_uint8x8_t.cpp:8
8        a_uint8x8 = vdup_n_u8(255);
(gdb) disas
Dump of assembler code for function main():
   0x00000000004005a0 <+0>:    sub sp, sp, #0x10
   0x00000000004005a4 <+4>:    mov w0, #0xffffffff             // #-1
   0x00000000004005a8 <+8>:    strb    w0, [sp,#7]
=> 0x00000000004005ac <+12>:    ldrb    w0, [sp,#7]
   0x00000000004005b0 <+16>:    dup v0.8b, w0
   0x00000000004005b4 <+20>:    str d0, [sp,#8]
   0x00000000004005b8 <+24>:    mov w0, #0x0                    // #0
   0x00000000004005bc <+28>:    add sp, sp, #0x10
   0x00000000004005c0 <+32>:    ret
End of assembler dump.
; 在语句 dup    v0.8b, w0 的下一条指令处设置断点,以观察 dup   v0.8b, w0 的执行效果
(gdb) b *0x00000000004005b4
Breakpoint 1 at 0x4005b4: file assign_uint8x8_t.cpp, line 8.
; 此时,还未执行 dup    v0.8b, w0。打印 v0 寄存器的值
(gdb) p $v0.b.u
$1 = {47 <repeats 16 times>}
(gdb) p $v0.b.u[0]
$2 = 47
(gdb) c
Continuing.

Breakpoint 2, main () at assign_uint8x8_t.cpp:8
8        a_uint8x8 = vdup_n_u8(255);
; 此时,已经执行了 dup    v0.8b, w0。打印 v0 寄存器的值
(gdb) p $v0.b.u
$3 = {255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}
(gdb) p $v0.b.u[0]
$4 = 255
(gdb) p $v0.b.u[7]
$5 = 255
(gdb) p $v0.b.u[8]
$6 = 0

可以看出 v0 寄存器的低位 8 字节分别被赋值为 255。

返回上一级


uint8x8_t 向量常数赋值(每个元素的值可以不同)

uint8x8_t vcreate_u8 (uint64_t __a);
void print_uint8x8_t(uint8x8_t val)
{
    std::printf("lane0=0x%x\n", vget_lane_u8(val, 0));
    std::printf("lane1=0x%x\n", vget_lane_u8(val, 1));
    std::printf("lane2=0x%x\n", vget_lane_u8(val, 2));
    std::printf("lane3=0x%x\n", vget_lane_u8(val, 3));
    std::printf("lane4=0x%x\n", vget_lane_u8(val, 4));
    std::printf("lane5=0x%x\n", vget_lane_u8(val, 5));
    std::printf("lane6=0x%x\n", vget_lane_u8(val, 6));
    std::printf("lane7=0x%x\n", vget_lane_u8(val, 7));
    std::printf("\n");
}

void assign_different_constant()
{
    std::printf("assign_different_constant function..............\n");

    uint8x8_t a_uint8x8 = vcreate_u8(0x12345678abcdef01);
    print_uint8x8_t(a_uint8x8);
}

输出结果为:

assign_different_constant function..............
lane0=0x1
lane1=0xef
lane2=0xcd
lane3=0xab
lane4=0x78
lane5=0x56
lane6=0x34
lane7=0x12

可以看出,参数__a的值从右到左每字节的内容依次赋值给向量的第一个元素到第八个元素。

返回上一级


向量内存赋值

返回上一级


uint8x8_t 向量内存赋值

uint8x8_t vld1_u8 (const uint8_t *a);
{
    // 源数据的有效元素数量等于8,合法
    uint8_t f_arr_uint8[] = {0xa10xa20xa30xa40xa50xa60xa70xa8};
    uint8x8_t f_uint8x8 = vld1_u8(f_arr_uint8);
    print_uint8x8_t(f_uint8x8);
}

{
    // 源数据的有效元素数量小于8,最后一个元素为非法读
    uint8_t a_arr_uint8[] = {0xa10xa20xa30xa40xa50xa60xa70xa8};
    uint8x8_t a_uint8x8 = vld1_u8(&a_arr_uint8[1]);
    print_uint8x8_t(a_uint8x8);
}

{
    // 源数据的有效元素数量大于8,合法
    uint8_t b_arr_uint8[] = {0xb00xb10xb20xb30xb40xb50xb60xb7
                                0xb80xb90xba0xbb0xbc0xbd0xbe0xbf};
    uint8x8_t b_uint8x8 = vld1_u8(b_arr_uint8);
    print_uint8x8_t(b_uint8x8);
}

{
    // 源数据的有效元素数量小于8,第五个元素到第八个元素为非法读
    uint8_t c_arr_uint8[] = {0xc10xc20xc30xc4};
    uint8x8_t c_uint8x8 = vld1_u8(c_arr_uint8);
    print_uint8x8_t(c_uint8x8);
}

{
    // 源数据的有效元素数量小于8,除第一个元素外其他元素为非法读
    uint8_t d_arr_uint8 = 0xd1;
    uint8x8_t d_uint8x8 = vld1_u8(&d_arr_uint8);
    print_uint8x8_t(d_uint8x8);
}

注:关于print_uint8x8_t函数的实现可以在其他示例中找到,此处省略。

(gdb) x/b &a_arr_uint8[0]+8
0x7ffffff320:    0xff
; 打印 uint8x8_t a_uint8x8 = vld1_u8(&a_arr_uint8[1]); 执行的结果
(gdb) p/x $v0.b.u
$4 = {0xa20xa30xa40xa50xa60xa70xa80xff0x00x00x00x00x00x00x00x0}
; 可以看出v0.b.u[7]的值为0xff,即内存地址为&a_arr_uint8[0]+8的值。验证了最后一个元素为非法读。

(gdb) x/4b c_arr_uint8+4
0x7ffffff31c:    0xb4    0xb5    0xb6    0xb7
; 打印 uint8x8_t c_uint8x8 = vld1_u8(c_arr_uint8); 执行的结果
(gdb) display/x $v0.b.u
1: /x $v0.b.u = {0xc10xc20xc30xc40xb40xb50xb60xb70x00x00x00x00x00x00x00x0}
; 可以看出v0.b.u[4]、v0.b.u[5]、v0.b.u[6]、v0.b.u[7]的值分别为0xb4、 0xb5、 0xb6、 0xb7
; 即内存起始地址为c_arr_uint8+4后面4字节的值。验证了第五个元素到第八个元素为非法读。

(gdb) x/7b &d_arr_uint8+1
0x7ffffff2c8:    0x19    0xf3    0xff    0xff    0x7f    0x00    0x00
; 打印 uint8x8_t d_uint8x8 = vld1_u8(&d_arr_uint8); 执行的结果
(gdb) display/x $v0.b.u
1: /x $v0.b.u = {0xd10x190xf30xff0xff0x7f0x00x00x00x00x00x00x00x00x00x0}
2: /x $v0.b.u = {0xd10x190xf30xff0xff0x7f0x00x00x00x00x00x00x00x00x00x0}
; 可以看出v0.b.u[1]、v0.b.u[2]、v0.b.u[3]、v0.b.u[4]、v0.b.u[5]、v0.b.u[6]、v0.b.u[7]的值
; 分别为0x19、 0xf3、 0xff、 0xff、 0x7f、 0x0、 0x0,即内存起始地址为&d_arr_uint8+1后面7字节的值。
; 验证了除第一个元素外其他元素为非法读。

上述示例验证了:vld1_u8()函数作用是将参数的值作为内存起始地址,无条件地将其后面的八字节的值赋值给向量。但如果源数据的有效元素数量小于8,会发生非法读

返回上一级


uint8x8x2_t 向量内存赋值

uint8x8x2_t vld2_u8 (const uint8_t * __a);
void print_uint8x8_t(uint8x8_t val)
{
    std::printf("lane0=0x%x\n", vget_lane_u8(val, 0));
    std::printf("lane1=0x%x\n", vget_lane_u8(val, 1));
    std::printf("lane2=0x%x\n", vget_lane_u8(val, 2));
    std::printf("lane3=0x%x\n", vget_lane_u8(val, 3));
    std::printf("lane4=0x%x\n", vget_lane_u8(val, 4));
    std::printf("lane5=0x%x\n", vget_lane_u8(val, 5));
    std::printf("lane6=0x%x\n", vget_lane_u8(val, 6));
    std::printf("lane7=0x%x\n", vget_lane_u8(val, 7));
    std::printf("\n");
}

void print_uint8x8x2_t(uint8x8x2_t data)
{
    std::printf("print val[0] ...\n");
    print_uint8x8_t(data.val[0]);
    std::printf("print val[1] ...\n");
    print_uint8x8_t(data.val[1]);
}

void assign_from_mem()
{
    std::printf("assign_from_mem function..............\n");

    uint8_t arr_uint8[] = {0xe10xe20xe30xe40xe50xe60xe70xe8
                            0xf10xf20xf30xf40xf50xf60xf70xf8};
    uint8x8x2_t a_uint8x8x2 = vld2_u8(arr_uint8);
    print_uint8x8x2_t(a_uint8x8x2);
}

输出结果为:

assign_from_mem function..............
print val[0] ...
lane0=0xe1
lane1=0xe3
lane2=0xe5
lane3=0xe7
lane4=0xf1
lane5=0xf3
lane6=0xf5
lane7=0xf7

print val[1] ...
lane0=0xe2
lane1=0xe4
lane2=0xe6
lane3=0xe8
lane4=0xf2
lane5=0xf4
lane6=0xf6
lane7=0xf8

返回上一级


向量单个元素内存赋值

返回上一级


uint8x8_t 向量单个元素内存赋值

#define vld1_lane_u8(a, b, c)
void print_uint8x8_t(uint8x8_t val)
{
    std::printf("lane0=0x%x\n", vget_lane_u8(val, 0));
    std::printf("lane1=0x%x\n", vget_lane_u8(val, 1));
    std::printf("lane2=0x%x\n", vget_lane_u8(val, 2));
    std::printf("lane3=0x%x\n", vget_lane_u8(val, 3));
    std::printf("lane4=0x%x\n", vget_lane_u8(val, 4));
    std::printf("lane5=0x%x\n", vget_lane_u8(val, 5));
    std::printf("lane6=0x%x\n", vget_lane_u8(val, 6));
    std::printf("lane7=0x%x\n", vget_lane_u8(val, 7));
    std::printf("\n");
}

void assign_lane()
{
    std::printf("assign_lane function..............\n");

    uint8_t a_arr_uint8[] = {0xa10xa20xa30xa40xa50xa60xa70xa8};
    uint8x8_t a_uint8x8;

    std::printf("set lane[0] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8, a_uint8x8, 0);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[1] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 1, a_uint8x8, 1);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[2] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 2, a_uint8x8, 2);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[3] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 3, a_uint8x8, 3);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[4] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 4, a_uint8x8, 4);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[5] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 5, a_uint8x8, 5);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[6] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 6, a_uint8x8, 6);
    print_uint8x8_t(a_uint8x8);

    std::printf("set lane[7] ...\n");
    a_uint8x8 = vld1_lane_u8(a_arr_uint8 + 7, a_uint8x8, 7);
    print_uint8x8_t(a_uint8x8);
}

输出结果为:

assign_lane function..............
set lane[0] ...
lane0=0xa1
lane1=0xed
lane2=0x21
lane3=0xe3
lane4=0x7f
lane5=0x0
lane6=0x0
lane7=0x0

set lane[1] ...
lane0=0xa1
lane1=0xa2
lane2=0x21
lane3=0xe3
lane4=0x7f
lane5=0x0
lane6=0x0
lane7=0x0

set lane[2] ...
lane0=0xa1
lane1=0xa2
lane2=0xa3
lane3=0xe3
lane4=0x7f
lane5=0x0
lane6=0x0
lane7=0x0

set lane[3] ...
lane0=0xa1
lane1=0xa2
lane2=0xa3
lane3=0xa4
lane4=0x7f
lane5=0x0
lane6=0x0
lane7=0x0

set lane[4] ...
lane0=0xa1
lane1=0xa2
lane2=0xa3
lane3=0xa4
lane4=0xa5
lane5=0x0
lane6=0x0
lane7=0x0

set lane[5] ...
lane0=0xa1
lane1=0xa2
lane2=0xa3
lane3=0xa4
lane4=0xa5
lane5=0xa6
lane6=0x0
lane7=0x0

set lane[6] ...
lane0=0xa1
lane1=0xa2
lane2=0xa3
lane3=0xa4
lane4=0xa5
lane5=0xa6
lane6=0xa7
lane7=0x0

set lane[7] ...
lane0=0xa1
lane1=0xa2
lane2=0xa3
lane3=0xa4
lane4=0xa5
lane5=0xa6
lane6=0xa7
lane7=0xa8

返回上一级


访问/存储向量的值


访问 uint8x8_t 的值

uint8_t vget_lane_u8(uint8x8_t __a, const int __b);
// 声明一个包含八个元素的向量,每个元素的数据类型为 uint8_t
uint8x8_t a_uint8x8;
// 将向量的每个元素赋值为 255
a_uint8x8 = vdup_n_u8(255);

// 分别打印变量 a_uint8x8 的八个元素的值(十六进制)
std::printf("a_uint8x8: lane0=0x%x\n", vget_lane_u8(a_uint8x8, 0));
std::printf("a_uint8x8: lane1=0x%x\n", vget_lane_u8(a_uint8x8, 1));
std::printf("a_uint8x8: lane2=0x%x\n", vget_lane_u8(a_uint8x8, 2));
std::printf("a_uint8x8: lane3=0x%x\n", vget_lane_u8(a_uint8x8, 3));
std::printf("a_uint8x8: lane4=0x%x\n", vget_lane_u8(a_uint8x8, 4));
std::printf("a_uint8x8: lane5=0x%x\n", vget_lane_u8(a_uint8x8, 5));
std::printf("a_uint8x8: lane6=0x%x\n", vget_lane_u8(a_uint8x8, 6));
std::printf("a_uint8x8: lane7=0x%x\n", vget_lane_u8(a_uint8x8, 7));

输出结果为:

a_uint8x8: lane0=0xff
a_uint8x8: lane1=0xff
a_uint8x8: lane2=0xff
a_uint8x8: lane3=0xff
a_uint8x8: lane4=0xff
a_uint8x8: lane5=0xff
a_uint8x8: lane6=0xff
a_uint8x8: lane7=0xff

返回上一级


存储 uint8x8_t 的值

void vst1_u8 (uint8_t *a, uint8x8_t b);
void store_uint8x8_t(uint8x8_t val_uint8x8)
{
    {
        // 目的数据的元素数量等于8,合法
        uint8_t f_arr_uint8[] = {0x10x20x30x40x50x60x70x8};
        std::printf("f_arr_uint8 ...\n");
        vst1_u8(f_arr_uint8, val_uint8x8);
    }

    {
        // 目的数据的元素数量等于8,但参数不是首元素的地址,最后一个元素为非法写
        uint8_t a_arr_uint8[] = {0xa10xa20xa30xa40xa50xa60xa70xa8};
        std::printf("a_arr_uint8 ...\n");
        vst1_u8(&a_arr_uint8[1], val_uint8x8);
    }

    {
        // 目的数据的元素数量大于8,只写数组b_arr_uint8的前八个元素,合法
        uint8_t b_arr_uint8[] = {0xb00xb10xb20xb30xb40xb50xb60xb7
                                    0xb80xb90xba0xbb0xbc0xbd0xbe0xbf};
        std::printf("b_arr_uint8 ...\n");
        vst1_u8(b_arr_uint8, val_uint8x8);
    }

    {
        // 目的数据的元素数量小于8,最后四个元素为非法写
        uint8_t c_arr_uint8[] = {0xc10xc20xc30xc4};
        std::printf("c_arr_uint8 ...\n");
        vst1_u8(c_arr_uint8, val_uint8x8);
    }

    {
        // 目的数据是标量,最后七个元素为非法写
        uint8_t d_arr_uint8 = 0xd1;
        std::printf("d_arr_uint8 ...\n");
        vst1_u8(&d_arr_uint8, val_uint8x8);
    }
}

注:参数val_uint8x8的值为:{0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8},参数val_uint8x8的值为:{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}。

(gdb) p/x &f_arr_uint8[0]
$1 = 0x7ffffff2c8
(gdb) display/9ubx 0x7ffffff2c8
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0x01    0x02    0x03    0x04    0x05    0x06    0x07    0x08
0x7ffffff2d0:    0xf1
(gdb) n
f_arr_uint8 ...
34            vst1_u8(f_arr_uint8, val_uint8x8);
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0x01    0x02    0x03    0x04    0x05    0x06    0x07    0x08
0x7ffffff2d0:    0xf1
(gdb) n
39            uint8_t a_arr_uint8[] = {0xa10xa20xa30xa40xa50xa60xa70xa8};
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xf1    0xf2    0xf3    0xf4    0xf5    0xf6    0xf7    0xf8
0x7ffffff2d0:    0xf1
; 可以看出,“第九个元素”(0x7ffffff2d0处的内容)没有被修改,验证了目的数据的元素数量等于8,合法。


(gdb) n
40            std::printf("a_arr_uint8 ...\n");
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xa1    0xa2    0xa3    0xa4    0xa5    0xa6    0xa7    0xa8
0x7ffffff2d0:    0xf1
(gdb) 
a_arr_uint8 ...
41            vst1_u8(&a_arr_uint8[1], val_uint8x8);
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xa1    0xa2    0xa3    0xa4    0xa5    0xa6    0xa7    0xa8
0x7ffffff2d0:    0xf1
(gdb) 
47                                        0xb80xb90xba0xbb0xbc0xbd0xbe0xbf};
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xa1    0xf1    0xf2    0xf3    0xf4    0xf5    0xf6    0xf7
0x7ffffff2d0:    0xf8
; 可以看出,“第八个元素”(0x7ffffff2d0处的内容)的值由0xf1变成了0xf8,但这个元素不是有效的,发生了非法写。


(gdb) n
48            std::printf("b_arr_uint8 ...\n");
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xb0    0xb1    0xb2    0xb3    0xb4    0xb5    0xb6    0xb7
0x7ffffff2d0:    0xb8
(gdb) 
b_arr_uint8 ...
49            vst1_u8(b_arr_uint8, val_uint8x8);
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xb0    0xb1    0xb2    0xb3    0xb4    0xb5    0xb6    0xb7
0x7ffffff2d0:    0xb8
(gdb) 
54            uint8_t c_arr_uint8[] = {0xc10xc20xc30xc4};
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xf1    0xf2    0xf3    0xf4    0xf5    0xf6    0xf7    0xf8
0x7ffffff2d0:    0xb8
(gdb) x/16ubx 0x7ffffff2c8
0x7ffffff2c8:    0xf1    0xf2    0xf3    0xf4    0xf5    0xf6    0xf7    0xf8
0x7ffffff2d0:    0xb8    0xb9    0xba    0xbb    0xbc    0xbd    0xbe    0xbf
; 可以看出,只有b_arr_uint8的前八个元素被修改了,合法


(gdb) n
55            std::printf("c_arr_uint8 ...\n");
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xc1    0xc2    0xc3    0xc4    0xf5    0xf6    0xf7    0xf8
0x7ffffff2d0:    0xb8
(gdb) 
c_arr_uint8 ...
56            vst1_u8(c_arr_uint8, val2_uint8x8);
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0xc1    0xc2    0xc3    0xc4    0xf5    0xf6    0xf7    0xf8
0x7ffffff2d0:    0xb8
(gdb) 
61            uint8_t d_arr_uint8 = 0xd1;
1: x/9xb 0x7ffffff2c8
0x7ffffff2c8:    0x01    0x02    0x03    0x04    0x05    0x06    0x07    0x08
0x7ffffff2d0:    0xb8
; 可以看出,c_arr_uint8[3]后面的四个元素被修改了,但这几个元素不是有效的,发生了非法写。


(gdb) undisplay 
Delete all auto-display expressions? (y or n) y
(gdb) n
61            uint8_t d_arr_uint8 = 0xd1;
(gdb) 
62            std::printf("d_arr_uint8 ...\n");
(gdb) p/x &d_arr_uint8
$1 = 0x7ffffff277
(gdb) display/8ubx 0x7ffffff277
1: x/8xb 0x7ffffff277
0x7ffffff277:    0xd1    0xc9    0xf2    0xff    0xff    0x7f    0x00    0x00
(gdb) n
d_arr_uint8 ...
63            vst1_u8(&d_arr_uint8, val_uint8x8);
1: x/8xb 0x7ffffff277
0x7ffffff277:    0xd1    0xc9    0xf2    0xff    0xff    0x7f    0x00    0x00
(gdb) 
65    }
1: x/8xb 0x7ffffff277
0x7ffffff277:    0xf1    0xf2    0xf3    0xf4    0xf5    0xf6    0xf7    0xf8
; 可以看出,&d_arr_uint8后面的七个元素被修改了,但这几个元素不是有效的,发生了非法写。

返回上一级


向量算术逻辑运算


向量加法运算

返回上一级


两个 uint8x8_t 相加

uint8x8_t vadd_u8(uint8x8_t a, uint8x8_t b);
uint8x8_t a_uint8x8, b_uint8x8;
a_uint8x8 = vdup_n_u8(0x12);
b_uint8x8 = vdup_n_u8(0x34);

uint8x8_t sum_uint8x8;
sum_uint8x8 = vadd_u8(a_uint8x8, b_uint8x8);

// 分别打印变量 sum_uint8x8 的八个元素的值(十六进制)
std::printf("sum_uint8x8: lane0=0x%x\n", vget_lane_u8(sum_uint8x8, 0));
std::printf("sum_uint8x8: lane1=0x%x\n", vget_lane_u8(sum_uint8x8, 1));
std::printf("sum_uint8x8: lane2=0x%x\n", vget_lane_u8(sum_uint8x8, 2));
std::printf("sum_uint8x8: lane3=0x%x\n", vget_lane_u8(sum_uint8x8, 3));
std::printf("sum_uint8x8: lane4=0x%x\n", vget_lane_u8(sum_uint8x8, 4));
std::printf("sum_uint8x8: lane5=0x%x\n", vget_lane_u8(sum_uint8x8, 5));
std::printf("sum_uint8x8: lane6=0x%x\n", vget_lane_u8(sum_uint8x8, 6));
std::printf("sum_uint8x8: lane7=0x%x\n", vget_lane_u8(sum_uint8x8, 7));

输出结果为:

sum_uint8x8: lane0=0x46
sum_uint8x8: lane1=0x46
sum_uint8x8: lane2=0x46
sum_uint8x8: lane3=0x46
sum_uint8x8: lane4=0x46
sum_uint8x8: lane5=0x46
sum_uint8x8: lane6=0x46
sum_uint8x8: lane7=0x46

返回上一级


下一篇:性能优化篇(4):NEON 优化案例——图像转换之 RGB 到 BGR(aarch64版)

上一篇:性能优化篇(2):小心“STL 低效率用法”所带来的性能开销

首页