Optimization ARM霓虹灯优化建议

Optimization ARM霓虹灯优化建议,optimization,assembly,arm,neon,Optimization,Assembly,Arm,Neon,出于学术目的,我想尝试编写以下算法的ARM NEON优化,甚至只是为了测试是否有可能获得任何性能改进。我认为这不是SIMD优化的好选择,因为结果合并在一起,失去了并行化增益 这是算法: const uchar* center = ...; int t0, t1, val; t0 = center[0]; t1 = center[1]; val = t0 < t1; t0 = center[2]; t1 = center[3]; val |= (t0 < t1) << 1

出于学术目的,我想尝试编写以下算法的ARM NEON优化,甚至只是为了测试是否有可能获得任何性能改进。我认为这不是SIMD优化的好选择,因为结果合并在一起,失去了并行化增益

这是算法:

const uchar* center = ...;

int t0, t1, val;
t0 = center[0]; t1 = center[1];
val = t0 < t1;
t0 = center[2]; t1 = center[3];
val |= (t0 < t1) << 1;
t0 = center[4]; t1 = center[5];
val |= (t0 < t1) << 2;
t0 = center[6]; t1 = center[7];
val |= (t0 < t1) << 3;
t0 = center[8]; t1 = center[9];
val |= (t0 < t1) << 4;
t0 = center[10]; t1 = center[11];
val |= (t0 < t1) << 5;
t0 = center[12]; t1 = center[13];
val |= (t0 < t1) << 6;
t0 = center[14]; t1 = center[15];
val |= (t0 < t1) << 7;

d[i] = (uchar)val;
假设为8位字符,第一个操作应在2个寄存器中交替加载所有t0和t1值

VCLT.U8 d2, d0, d1
对所有比较进行“小于”的单个操作。注意:我已经读到VCLT只可能使用#0常量作为第二个操作数,所以这必须在>=中反转。阅读ARM文档,我认为每8位值的结果将是“全部1”表示真(11111111),或“全部0”表示假(00000000)

此右移将删除寄存器8位“单元”中8个值中的7个(主要是删除7个)。我使用了d4,因为下一步将是q2中映射的第一个d寄存器

现在问题开始了:移位和移位

VSHLL.U8 q2[1], d4[1], 1
VSHLL.U8 q2[2], d4[2], 2
...
VSHLL.U8 q2[7], d4[7], 7
我可以想象只有这样(如果可能使用[偏移量])才能实现左移。根据文件规定,应指定Q2而不是d4

VORR(.U8) d4[0], d4[1], d4[0]
VORR(.U8) d4[0], d4[2], d4[0]
...
VORR(.U8) d4[0], d4[7], d4[0]
最后一步应该给出结果

VST1.8 d4[0], [d[i] addr]
结果的简单存储

VST1.8 d4[0], [d[i] addr]
这是我第一次使用ARM NEON,所以很多假设可能是不正确的。帮助我理解可能出现的错误,并在可能的情况下提出更好的解决方案

编辑: 这是建议解决方案之后的最终工作代码:

__asm__ __volatile ("VLD2.8 {d0, d1}, [%[ordered_center]] \n\t"
"VCGT.U8 d2, d1, d0 \n\t"
"MOV r1, 0x01 \n\t"
"MOV r2, 0x0200 \n\t"
"ORR r2, r2, r1 \n\t"
"MOV r1, 0x10 \n\t"
"MOV r3, 0x2000 \n\t"
"ORR r3, r3, r1 \n\t"
"MOVT r2, 0x0804 \n\t"
"MOVT r3, 0x8040 \n\t"
"VMOV.32 d3[0], r2 \n\t"
"VMOV.32 d3[1], r3 \n\t"
"VAND d0, d2, d3 \n\t"
"VPADDL.U8 d0, d0 \n\t"
"VPADDL.U16 d0, d0 \n\t"
"VPADDL.U32 d0, d0 \n\t"
"VST1.8 d0[0], [%[desc]] \n\t"
:
: [ordered_center] "r" (ordered_center), [desc] "r" (&desc[i])
: "d0", "d1", "d2", "d3", "r1", "r2", "r3");

比较之后,您有一个由8个布尔值组成的数组,由
0xff
0x00
表示。SIMD比较(在任何体系结构上)产生这些值的原因是为了使它们对位掩码操作(和/或在NEON的情况下为位选择)有用,以便您可以快速将结果转换为任意值,而无需乘法

因此,与其将它们减少到
1
0
并四处移动,不如使用常量
0x8040201008040201
来屏蔽它们。然后,每个通道包含与其在最终结果中的位置相对应的位。您可以将常量预加载到另一个寄存器中(我将使用
d3

然后,要组合结果,可以使用
VPADD
(而不是
),它将组合相邻的车道对,
d0[0]=d0[0]+d0[1]
d0[1]=d0[2]+d0[3]
,等等。。。由于位模式不重叠,因此没有进位和加法以及or。此外,由于输出是输入的一半,我们必须在下半部分填充垃圾。为此,我使用了第二份
d0

您需要执行三次add操作才能合并所有列

VPADD.u8 d0, d0, d0
VPADD.u8 d0, d0, d0
VPADD.u8 d0, d0, d0
现在结果将出现在
d0[0]

如您所见,
d0
还有七个结果的空间;
VPADD
操作的一些通道一直在处理垃圾数据。如果您可以一次获取更多的数据,并在执行过程中输入这些额外的工作,这样就不会浪费任何算术运算,那就更好了


编辑

假设循环展开四次;结果为
d4
d5
d6
d7
;前面提到的常数应该加载到,比如说,
d30
d31
,然后可以使用一些
q
寄存器算法:

VAND q0, q2, q15
VAND q1, q3, q15

VPADD.u8 d0, d0, d1
VPADD.u8 d2, d2, d3
VPADD.u8 d0, d0, d2
VPADD.u8 d0, d0, d0 
最终结果为d0[0..3],或仅为d0[0]中的32位值


似乎有很多寄存器可以进一步展开,但我不知道有多少寄存器将用于其他计算。

比较后,您有一个由8个布尔值组成的数组,表示为
0xff
0x00
。SIMD比较(在任何体系结构上)产生这些值的原因是为了使它们对位掩码操作(和/或在NEON的情况下为位选择)有用,以便您可以快速将结果转换为任意值,而无需乘法

因此,与其将它们减少到
1
0
并四处移动,不如使用常量
0x8040201008040201
来屏蔽它们。然后,每个通道包含与其在最终结果中的位置相对应的位。您可以将常量预加载到另一个寄存器中(我将使用
d3

然后,要组合结果,可以使用
VPADD
(而不是
),它将组合相邻的车道对,
d0[0]=d0[0]+d0[1]
d0[1]=d0[2]+d0[3]
,等等。。。由于位模式不重叠,因此没有进位和加法以及or。此外,由于输出是输入的一半,我们必须在下半部分填充垃圾。为此,我使用了第二份
d0

您需要执行三次add操作才能合并所有列

VPADD.u8 d0, d0, d0
VPADD.u8 d0, d0, d0
VPADD.u8 d0, d0, d0
现在结果将出现在
d0[0]

如您所见,
d0
还有七个结果的空间;
VPADD
操作的一些通道一直在处理垃圾数据。如果您可以一次获取更多的数据,并在执行过程中输入这些额外的工作,这样就不会浪费任何算术运算,那就更好了


编辑

假设循环展开四次;结果为
d4
d5
d6
d7
;前面提到的常数应该加载到,比如说,
d30
d31
,然后可以使用一些
q
寄存器算法:

VAND q0, q2, q15
VAND q1, q3, q15

VPADD.u8 d0, d0, d1
VPADD.u8 d2, d2, d3
VPADD.u8 d0, d0, d2
VPADD.u8 d0, d0, d0 
最终结果为d0[0..3],或仅为d0[0]中的32位值

似乎有很多寄存器可以进一步展开,但我不知道在其他计算中会用到多少寄存器

  • 加载一个值为0x80402010008040201的d寄存器
  • 使用vclt的结果进行vand
  • vpaddl.u8从2开始)
  • vpaddl.u16来自
    int /* bool, whatever ... */ val[8] = {
        center[0] < center[1],
        center[2] < center[3],
        center[4] < center[5],
        center[6] < center[7],
        center[8] < center[9],
        center[10] < center[11],
        center[12] < center[13],
        center[14] < center[15]
    };
    d[i] = extract_mask(val);