2014-10-28 3 views
1

다음 코드는 ARM NEON 명령어를 사용하여 최적화하고 싶습니다. 어떻게 구현할 수 있습니까? 답변ARM NEON 명령어를 사용하여 배열의 최소 및 최대 값 찾기

unsigned char someVector[] = {1, 2, 4, 1, 2, 0, 8, 100}; 
unsigned char maxVal = 0, minVal = 255; 
for (int i = 0; i < sizeof(someVector); i++) 
{ 
    if (someVector[i] < minVal) 
    { 
     minVal = someVector[i]; 
    } 
    else if (someVector[i] > maxVal) 
    { 
     maxVal = someVector[i]; 
    } 
} 
+0

infocenter.arm.com에서 어셈블리 설명서를 다운로드 할 수 있습니다. 그런 다음 'vmin'및 'vmax'지침을 볼 수 있습니다. –

+0

귀하의 의견을 보내 주셔서 감사합니다. 나는 vmin과 vmax를 알고 있지만 두 개의 레지스터에서 각 레인을 비교합니다. 그렇다면 최소값과 최대 값을 쌍으로 설정해야하지만 모든 값을 초과하지는 마십시오. – Zoli

+0

3 개의 연속적인 vpmin과 vpmax가 나머지 작업을 수행합니다. 그러나 명령어 대기 시간이 성능을 심각하게 저하 시키므로 ARM 정수 코어로이 작업을 수행하는 것이 좋습니다. NEON은 이러한 작은 데이터를 우선 처리하지 않습니다. –

답변

1

에 대한 감사합니다 다음은 큰 배열의 최소 및 최대를 찾기 위해 어떻게 최적화 된 예이다. 이 함수는 크기가 128보다 작 으면 단순히 반환합니다.

/* 
* minmax.S 
* 
* Created on: 2014. 10. 29. 
*  Author: Jake Lee 
*/ 


// unsigned int minmax(unsigned char *pSrc, unsigned int size); 

    .text 
    .arm 
    .global minmax 

    pSrc .req r0 
    size .req r1 

    qmin1 .req q0 
     dmina .req d0 
     dminb .req d1 

    qmax1 .req q1 
     dmaxa .req d2 
     dmaxb .req d3 

    qmin2 .req q2 
    qmax2 .req q3 

    .align 5 
    .func 
minmax: 
    subs size, size, #128 
    bxmi lr 
    vmov.i8  qmin1, #0xff 
    vmov.i8  qmax1, #0 
    vmov.i8  qmin2, #0xff 
    vmov.i8  qmax2, #0 

    .align 5 
1: 
    vld1.8  {q8, q9}, [pSrc]! 
    vld1.8  {q10, q11}, [pSrc]! 
    vld1.8  {q12, q13}, [pSrc]! 
    vld1.8  {q14, q15}, [pSrc]! 
    subs size, size, #128 
    pld  [pSrc, #64*3] 
    pld  [pSrc, #64*4] 
    vmin.u8  qmin1, q8 
    vmax.u8  qmax1, q8 
    vmin.u8  qmin2, q9 
    vmax.u8  qmax2, q9 
    vmin.u8  qmin1, q10 
    vmax.u8  qmax1, q10 
    vmin.u8  qmin2, q11 
    vmax.u8  qmax2, q11 
    vmin.u8  qmin1, q12 
    vmax.u8  qmax1, q12 
    vmin.u8  qmin2, q13 
    vmax.u8  qmax2, q13 
    vmin.u8  qmin1, q14 
    vmax.u8  qmax1, q14 
    vmin.u8  qmin2, q15 
    vmax.u8  qmax2, q15 
    bpl  1b 

// deal width residuals (size % 128) 
    cmp  size, #-128 
    addgt pSrc, pSrc, size 
    bgt  1b 

// shrink to sixteen 
    vmin.u8  qmin1, qmin2 
    vmax.u8  qmax1, qmax2 
// shrink to eight 
    vpmin.u8 dmina, dmina, dminb 
    vpmax.u8 dmaxa, dmaxa, dmaxb 
// shrink to four 
    vpmin.u8 dmina, dmina, dminb 
    vpmax.u8 dmaxa, dmaxa, dmaxb 
// shrink to two 
    vpmin.u8 dmina, dmina, dminb 
    vpmax.u8 dmaxa, dmaxa, dmaxb 
// shrink to one 
    vpmin.u8 dmina, dmina, dminb 
    vpmax.u8 dmaxa, dmaxa, dmaxb 

    vmov r0, dmina[0] 
    vmov r1, dmaxa[0] 

    and  r0, r0, #0xff 
    and  r1, r1, #0xff 
    orr  r0, r0, r1, lsl #16 
    bx  lr 
    .endfunc 
    .end 

반환 값은 부호없는 int입니다. 단지 약간만 수정,

result = minmax(pSrc, size); 
min = result & 0xff; 
max = result >> 16; 
+0

Jake에게 감사드립니다. 실제로 내 배열은 이미지이고, someVector는 단지 예일뿐입니다. 혼란에 대해 사과드립니다. 배열이 상당히 길어집니다. 이미지의 경우, 8 픽셀 단위로 vmin 및 vmax를 호출하고 마지막에는 vpmin 및 vpmax를 3 번 ​​호출합니까? – Zoli

+0

내 코드가 한 번에 16 바이트를 처리합니다. (q 레지스터) 반복 당 128 바이트를 수행합니다. 내장 함수로 작업하려고 시도하는 것 같지만 위의 어셈블리 버전의 절반도되지 않습니다. –

+0

@ Jake'Alquimista'LEE 왜 plds + 64 * 3 및 + 64 * 4입니까? "pSrc"가로드 될 다음 주소가 아니므로 pSrc 자체를 미리로드하지 않는 이유는 무엇입니까? 그리고 두 개의 다른 레지스터에 대해 pld를 연속적으로 사용하여 더 빨리 만들 수 있습니까? 도움이되는 문서는 없습니다. –

1

GCC 것 자동 벡터화이 : 낮은 16 비트 분 이상 사람의 최대를 포함한다.

unsigned char someVector[256] = { 1, 2, 4, 1, 2, 0, 8, 100 }; 
unsigned char maxVal = 0, minVal = 255; 

void f(void) 
{ 
    unsigned char mn = 255, mx = 0; 
    for (int i = 0; i < sizeof(someVector); i++) { 
     if (someVector[i] < mn) { 
      mn = someVector[i]; 
     } 
     if (someVector[i] > mx) { 
      mx = someVector[i]; 
     } 
    } 
    maxVal = mx; 
    minVal = mn; 
} 

는 NEON의 내장 함수 또는 어셈블러를 작성하는 경우 당신은 GCC보다 더 잘 할 수

$ arm-unknown-linux-gnueabihf-gcc -O3 -std=c11 -mfpu=neon -c test.c 

또는

$ arm-unknown-linux-gnueabihf-gcc -O2 -ftree-vectorize -std=c11 -mfpu=neon -c test.c 

로 컴파일합니다.

+0

감사합니다. Charles, 불행히도 저는 WinCE를 사용하고 gcc를 사용하지 않았습니다. – Zoli

+0

@Zoli MS의 ARM 컴파일러는 GCC보다 낫다. –