NEON 优化 C++ 混响算法

普通的串行版本 和优化思路在这里。

跟 AVX2 不一样的大概是两点:

  • NEON 指令只可以同时处理 4 个 float 数据
  • NEON 有自己的函数集

优化后的 NEON 版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
size_t samples = file.file_size / 4;
size_t ir_samples = sizeof(ir) / 4;
for (size_t i = 0; i < samples; ++i)
{
float32x4_t out = { 0 };
for (size_t j = 0; j < ir_samples && i >= j; j += 4)
{
float32x4_t ir_x4 = vld1q_f32(ir + j);
float32x4_t in;
switch (i - j)
{
case 0:
{
float temp[4] = {file.in_file_data[0], 0, 0, 0};
in = vld1q_f32(temp);
break;
}
case 1:
{
float temp[4] = {file.in_file_data[1], file.in_file_data[0], 0, 0};
in = vld1q_f32(temp);
break;
}
case 2:
{
float temp[4] = {file.in_file_data[2], file.in_file_data[1], file.in_file_data[0], 0};
in = vld1q_f32(temp);
break;
}
default:
// float temp[4] = {file.in_file_data[i-j], file.in_file_data[i-j-1], file.in_file_data[i-j-2], file.in_file_data[i-j-3]};
// in = vld1q_f32(temp);
in = vld1q_f32(file.in_file_data + i - j - 3);
in = vrev64q_f32(in);
break;
}
out = vmlaq_f32(out, in, ir_x4);
}
file.out_file_data[i] += vgetq_lane_f32(out, 0);
file.out_file_data[i] += vgetq_lane_f32(out, 1);
file.out_file_data[i] += vgetq_lane_f32(out, 2);
file.out_file_data[i] += vgetq_lane_f32(out, 3);
}

NEON 函数集跟 AVX2 略有不同,只有顺序加载的 load ( vld1q_f32() ),没有逆序、离散加载的 set,所以需要构造一个临时的 float 数组,逆序赋值,再传入 load 函数。

不需要补零时,构造临时 float 数组再 load,效率明显低于上边代码示例里 default 块的实现方式,即先一次性 load 4个数据后再反转。在我的测试用例里,单这一个改动就提升了约 10% 的性能。

完整的 Android 测试工程在这里

评论