性能优化篇(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 vdup_n_u8(uint32_t a);
作用
uint8x8_t
的向量中每个元素的值设置为参数a
的值。注意事项
a
的值大于 255 时会发生”整型截断“;每个元素的赋值互不影响。代码示例,assign_uint8x8_t.cpp:
#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 vcreate_u8 (uint64_t __a);
作用
a
赋值给类型为uint8x8_t
的向量。注意事项
__a
的值从右到左每字节的内容依次赋值给向量的第一个元素到第八个元素(即lane[0]
到lane[7]
)。代码示例
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 vld1_u8 (const uint8_t *a);
作用
a
的后面八字节的内容赋值给类型为uint8x8_t
的向量。注意事项
a
的有效元素数量小于8,那么会发生非法读。代码示例
{
// 源数据的有效元素数量等于8,合法
uint8_t f_arr_uint8[] = {0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8};
uint8x8_t f_uint8x8 = vld1_u8(f_arr_uint8);
print_uint8x8_t(f_uint8x8);
}
{
// 源数据的有效元素数量小于8,最后一个元素为非法读
uint8_t a_arr_uint8[] = {0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8};
uint8x8_t a_uint8x8 = vld1_u8(&a_arr_uint8[1]);
print_uint8x8_t(a_uint8x8);
}
{
// 源数据的有效元素数量大于8,合法
uint8_t b_arr_uint8[] = {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf};
uint8x8_t b_uint8x8 = vld1_u8(b_arr_uint8);
print_uint8x8_t(b_uint8x8);
}
{
// 源数据的有效元素数量小于8,第五个元素到第八个元素为非法读
uint8_t c_arr_uint8[] = {0xc1, 0xc2, 0xc3, 0xc4};
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 = {0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
; 可以看出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 = {0xc1, 0xc2, 0xc3, 0xc4, 0xb4, 0xb5, 0xb6, 0xb7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
; 可以看出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 = {0xd1, 0x19, 0xf3, 0xff, 0xff, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
2: /x $v0.b.u = {0xd1, 0x19, 0xf3, 0xff, 0xff, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
; 可以看出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 vld2_u8 (const uint8_t * __a);
作用
__a、__a+2、__a+4、… __a+14
的内容分别赋值给第一个向量的lane[0]、lane[1]、…、lane[7]
,内存地址为__a+1、__a+3、__a+5、… __a+15
的内容分别赋值给第二个向量的lane[0]、lane[1]、…、lane[7]
。代码示例
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[] = {0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8};
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
#define vld1_lane_u8(a, b, c)
作用
a
为指向数据类型为uint8_t
的指针;参数b
为指向数据类型为uint8x8_t
的向量;参数c
为向量中元素的索引,合法范围为0 <= c <=7
。代码示例
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[] = {0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8};
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
uint8_t vget_lane_u8(uint8x8_t __a, const int __b);
作用
uint8x8_t
向量(由参数__a
确定)的第n
个元素(由参数__b
确定)的值。注意事项
参数__b
指明访问向量中第几个元素的值,有效范围为0 <= b <= 7
。
如果参数__b
的值不在有效范围,在编译时会报如下错误——error: lane 8 out of range 0 - 7
。
参数__b
的值必须在编译期确定。也就是说,参数__b
只能是常数。如果不是,编译时会报如下错误——error: lane index must be a constant immediate
。
代码示例
// 声明一个包含八个元素的向量,每个元素的数据类型为 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
void vst1_u8 (uint8_t *a, uint8x8_t b);
作用
uint8x8_t
向量(由参数b
确定)的值存储到内存起始地址为a
后面八字节的内存中。注意事项
a
所指向的合法内存必须不小于八字节,否则会产生非法写。代码示例
void store_uint8x8_t(uint8x8_t val_uint8x8)
{
{
// 目的数据的元素数量等于8,合法
uint8_t f_arr_uint8[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
std::printf("f_arr_uint8 ...\n");
vst1_u8(f_arr_uint8, val_uint8x8);
}
{
// 目的数据的元素数量等于8,但参数不是首元素的地址,最后一个元素为非法写
uint8_t a_arr_uint8[] = {0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8};
std::printf("a_arr_uint8 ...\n");
vst1_u8(&a_arr_uint8[1], val_uint8x8);
}
{
// 目的数据的元素数量大于8,只写数组b_arr_uint8的前八个元素,合法
uint8_t b_arr_uint8[] = {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf};
std::printf("b_arr_uint8 ...\n");
vst1_u8(b_arr_uint8, val_uint8x8);
}
{
// 目的数据的元素数量小于8,最后四个元素为非法写
uint8_t c_arr_uint8[] = {0xc1, 0xc2, 0xc3, 0xc4};
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[] = {0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8};
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 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf};
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[] = {0xc1, 0xc2, 0xc3, 0xc4};
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 vadd_u8(uint8x8_t a, uint8x8_t b);
作用
uint8x8_t
向量的对应位置元素相加,任意对应位置元素相加的结果(可能会进位或溢出)不会影响其他位置相加的结果。代码示例
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版)