如何有效地使用Neon Extension反转汇编语言ARM中的数组?

时间:2017-10-29 10:13:33

标签: c++ assembly arm neon

我正在处理一些图像处理功能,我需要以最快的方式反转字节数组。如果我试着解释我的实际功能,那就不合适了。这就是为什么我要简化问题标准。

Input Array: 
37 3B 29 32 C5 E3 F3 5E 04 E2 CA B8 A1 1F 64 1D 
E5 6F 7B 2C EA 6A FD 1F A5 6B 8F FA FB 7A F4 2A 
DC 08 6D DB B8 D4 77 5D A2 44 E6 8A 59 9C 7D C2 
8E FB C6 2A F8 EC 96 ED DC F8 00 2D 63 4C A4 F9
Length: 64
Output Array: 
F9 A4 4C 63 2D 00 F8 DC ED 96 EC F8 2A C6 FB 8E 
C2 7D 9C 59 8A E6 44 A2 5D 77 D4 B8 DB 6D 08 DC 
2A F4 7A FB FA 8F 6B A5 1F FD 6A EA 2C 7B 6F E5 
1D 64 1F A1 B8 CA E2 04 5E F3 E3 C5 32 29 3B 37

在C ++这样的高级语言中完成这项工作非常容易。在C ++中,此实现可能如下所示:

void Reverse_array(unsigned char* pInData, int iLen, unsigned char* pOutData)
{
    int indx = 0;
    for(int i=iLen-1; i>=0; i--)
    {
        pOutData[indx++] = pInData[i];
    }
}

但我真的需要找到最有效和最优化的解决方案来完成这项工作。由于此任务将在移动设备中执行,因此我决定使用ARM中的原始汇编语言和Neon Extension实现。现在,我将分享我的努力来实现这项任务(仍然不完整)。

NEON_ASM_FUNC_BEGIN Reverse_array_arm_neon
push {r2-r8, lr}
#r0 First parameter, This is the address of <pInData>
#r1 Second Parameter, This is the iLen
#r2 Third Parameter, This is the address of <pOutData>
add r2, r2, r1
ands r3, r1, #7
add r2, r2, #8

loop_Reverse:
vld1.u8 {d0}, [r0]!
vrev64.u8 d1, d0
sub r2, r2, #16
vst1.u8 {d1}, [r2]!
subs r1, #8
bne loop_Reverse

pop {r2-r8, pc}
NEON_ASM_FUNC_END

我还检查了StackOverFlow中的How to reverse an array in assembly language ARM?Reversing an array in assembly解决方案,但我仍需要更多有关此实现的知识。

  1. 对于这个问题,是否可以在arm汇编语言中编写比c ++更快的函数?
  2. 使用NEON扩展功能实现此任务的正确方法是什么? (我的功能不完整,因为它不会以不同的长度工作,而不能被8整除。)
  3. 任何想法或信息对我都有帮助。谢谢。

2 个答案:

答案 0 :(得分:4)

如果In和Out不重叠,并且iLen大于64,则下面的代码应该有效:

// Written by Jake 'Alquimista' LEE

    .syntax unified
    .arch   armv7-a
    .fpu    neon
    .text
    .global Reverse_array_arm_neon

// void Reverse_array_arm_neon(unsigned char* pInData, int iLen, unsigned char* pOutData);


pSrc    .req    r0
iLen    .req    r1
pDst    .req    r2
postInc .req    r3

.balign 32
.func
Reverse_array_arm_neon:
    add     pDst, pDst, iLen
    mov     postInc, #-32
    sub     pDst, pDst, #32
    sub     iLen, iLen, #64     // "withholding tax"

.balign 32
1:
    vld1.8      {d16, d17, d18, d19}, [pSrc]!
    vld1.8      {d20, d21, d22, d23}, [pSrc]!
    subs    iLen, iLen, #64
    pld [pSrc, #64]

    vrev64.8    q8, q8
    vrev64.8    q9, q9
    vrev64.8    q10, q10
    vrev64.8    q11, q11

    vswp        d19, d16
    vswp        d18, d17
    vswp        d23, d20
    vswp        d22, d21

    vst1.8      {d16, d17, d18, d19}, [pDst], postInc
    vst1.8      {d20, d21, d22, d23}, [pDst], postInc

    bpl     1b
    add     pSrc, pSrc, iLen
    cmp     iLen, #-64
    sub     pDst, pDst, iLen
    bxle    lr      // return
    b       1b
.endfunc
.end
  • 您必须保留的唯一寄存器是:r4-r11,lrq4-q7,并且只有在您必须使用它们时才会保留。
  • 如果您没有保留任何内容并损坏bx lr
  • ,则可以lr返回
  • 您可以按照我的方式最有效地处理“残差”(循环后的负循环计数器,“预扣税”,bpladd/sub。)
  • 您应该将主循环与32字节对齐,以实现I-Cache效率。

答案 1 :(得分:1)

首先,我希望尊重 Jake&#39; Alquimista&#39; LEE 我从他的回答中学到了很多东西。在这里,我将分享这个问题的替代解决方案。

.macro NEON_ASM_FUNC_BEGIN
.syntax unified
.text
.extern printf
.align 2
.arm
.globl _$0
_$0:
.endm

.macro NEON_ASM_FUNC_END
mov pc, lr
.endm

NEON_ASM_FUNC_BEGIN Reverse_array_arm_neon
push {r3-r8, lr}
pInData     .req    r0
iLen        .req    r1
pOutData    .req    r2
iOverlap    .req    r3
iTemp       .req    r4

add         pOutData,   pOutData,   iLen
add         pOutData,   pOutData,   #8
ands        iOverlap,   iLen,       #7

beq         loop_Reverse //if(iLen % 8 == 0) goto loop_Reverse

sub         pOutData,   pOutData,   #16
vld1.u8     {d0},       [pInData]!
vrev64.u8   d1,         d0
vst1.u8     {d1},       [pOutData]!
subs        iLen,       iLen,       iOverlap
beq Reverse_array_arm_neon_completed //if(iLen == 0) go to end

mov         iTemp,      #8
sub         iTemp,      iTemp,      iOverlap
sub         pInData,    pInData,    iTemp
add         pOutData,   pOutData,   iTemp

loop_Reverse:
    vld1.u8     {d0},       [pInData]!
    vrev64.u8   d1,         d0
    sub         pOutData,   pOutData, #16
    vst1.u8     {d1},       [pOutData]!
    subs        iLen,        #8
    bne loop_Reverse

Reverse_array_arm_neon_completed:

pop {r3-r8, pc}
NEON_ASM_FUNC_END

我在Apple Ipod touch第5代(32位/ Cortex A9 / ARMv7-A平台)设备上测试了此功能。此函数适用于每个可能的数组长度。希望能帮助到你。