본문 바로가기

Programming/NEON

[NEON 산술 연산과 확장

NEON 에서는 벡터 변수 사이의 기본 산술 연산을 지원하고, 포화 더하기와 인접 레인 더하기 등 NEON만의 독특한 산술 연산도 지원한다. 또한 최댓값 및 최솟값 연산과 Long, Wide, Narrow, 포화와 관련된 산술 연산을 지원한다.


NEON에서는 나누기와 제곱근 연산은 지원하지 않아서 Shift 연산이나 역수를 이용해야 한다.


1. NEON 기본 산술 연산 (더하기, 빼기, 곱하기, 레인 최댓값, 최솟값, 인접 레인 더하기 함수)

(1) 더하기 함수

uint16x8_t r = vaddq_u16(uint16x8_t a, uint16x8_t b)   a + b 를 r벡터에 저장


(2) 빼기 함수

uint16x8_t r = vsubq_u16(uint16x8_t a, uint16x8_t b)   a - b 를 r벡터에 저장


(3) 곱하기 함수

uint16x8_t r = vmulq_u16(uint16x8_t a, uint16x8_t b)   a * b 를 r벡터에 저장

 ※ 곱하기 연산의 오버플로를 방지하려면 레인의 크기를 확장하거나 포화연산을 사용하는 것이 좋다.


(4) 나눗셈

NEON에서는 나눗셈 연산을 지원하지 않는다. 그래서 Shift 연산으로 나눗셈을 대체하는 방법을 이용하거나 역수를 이용한 나눗셈을 구현해야 한다. 단 역수를 이용한 나눗셈은 부호 있는 정수와 부호 있는 실수만 가능하므로 변수형을 부호 있는 정수나 실수로 벡터 형변환하여 나눗셈을 구현해야 한다.


(5) 인접 레인 더하기 함수

uint16x4_t r = vpadd_u16(uint16x4_t a, uint16x4_t b) 

 a[3] + a[2] = r[3]  , a[1] + a[0] = r[2] 

 b[3] + b[2] = r[1]  , b[1] + b[0] = r[0]

인접 레인 사이의 더하기 연산


(6) 최댓값 함수

uint16x8_t r = vmaxq_u16(uint16x8_t a, uint16x8_t b)

a레인과 b레인을 각각 순서대로 비교해 큰 값을 r 레인에 저장

예) a[0] 과 b[0]을 비교해 큰 값을 r[0]에 저장 ...


(7) 최솟값 함수

uint16x8_t r = vminq_u16(uint16x8_t a, uint16x8_t b)

최댓값 함수와 마찬가지로 a레인과 b레인을 각각 순서대로 비교해 작은 값을 r레인에 저장


2. 산술 연산의 확장

(1) Long 더하기 함수 : 같은 레인을 연산해 2배 크기의 벡터에 저장

uint16x8_t r = vaddl_u8(uint8x8_t a, uint8x8_t b)


(2) Wide 더하기 함수 : 다른 크기의 벡터 사이에 더하기 연산을 하고 그 결과를 쿼드 워드 벡터에 저장

uint16x8_t r = vaddw_u8(uint16x8_t a, uint8x8_t b)


(3) 포화 더하기 함수 : 같은 크기의 벡터 사이에 더하기 연산을 한다. 단 오버플로가 발생하면 bit의 최댓값 또는 최솟값으로 저장

uint8x8_t r = vqadd_s8(int8x8_t a, int8x8_t b)


(4) Narrow 더하기 함수 : 같은 크기의 벡터 사이에 더하기 연산을 하고 절반 크기의 벡터에 그 결과를 저장한다.

                                 이때 결과는 상위비트만 저장된다.(레인의 크기가 반으로 줄어듬)

uint8x8_t vaddhn_u16(uint16x8_t a, uint16x8_t b)


(5) 벡터 2개의 평균 구하기 함수

uint16x8_t vrhaddq_u16(uint16x8_t a, uint16x8_t b) (a+b)>>1의 연산 수

NEON 에는 나누기 연산이 없으므로 >>1 연산(비트 연산자)를 사용하여 나누기 2를 대신한다.


(6) Long 빼기 함수

uint16x8_t r = vsubl_u8(uint8x8_t a, uint8x8_t b)

같은 크기 2개의 벡터를 빼기 연산하고 그 결과를 2배 크기의 벡터에 저장한다. (더블워드 -> 쿼드워드)


(7) Wide 빼기 함수

uint16x8_t r = vsubw_u8(uint16x8_t a, uint8x8_t b)

다른 크기의 벡터 변수 사이에 빼기 연산을 하고, 그 결과를 쿼드 워드 벡터 변수에 저장(쿼드워드-더블워드 = 쿼드워드)


(8) 포화 빼기 함수

int8x8_t r = vqsub_s8(int8x8_t a, int8x8_t b)

같은 크기의 벡터 사이에 빼기 연산을 하고 그 결과를 벡터 변수에 저장한다. 만약 오버플로가 발생하면 bit의 최댓값 또는 최솟값으로 저장한다.


(9) Narrow 빼기 함수

int8x8_t r = vsubhn_u16(uint16x8_t a, uint16x8_t b) 

같은 크기의 벡터 사이에 빼기 연산을 하고 절반 크기의 벡터에 결과를 저장한다. Narrow 더하기 함수와 마찬가지로 이때 결과는 상위비트만 저장된다.


(10) 빼기 후 평균 함수

uint16x8_t r = vhsub_u16(uint16x8_t a, uint16x8_t b) (a-b)>>1

벡터 2개를 뺀 값의 평균을 구한다.


(11) Long 곱하기 함수

uint16x8_t r = vmull_u8(uint8x8_t a, uint8x8_t b)

같은 크기의 벡터 사이에 곱하기 연산 후 그 결과를 2배 크기의 벡터에 저장한다.


(12) 곱한 후 가감 함수

uint16x8_t r = vmlaq_u16(uint16x8_t a, uint16x8_t b, uint16x8_t c)  r = a+(b*c)

3개의 벡터를 이용하여 벡터 사이에 곱하기 연산과 더하기 연산을 실행한다.

벡터 b와 c를 곱한 결과에 벡터 a의 값을 더하고 그 결과를 r에 저장한다.


uint16x8_t r = vmlsq_u16(uint16x8_t a, uint16x8_t b, uint16x8_t c)  r = a-(b*c)

벡터 b와 c를 곱한 결과에 벡터 a를 빼 그 결과를 r에 저장한다.


(13) Long 인접 레인 더하기 함수

uint16x4_t r = vpaddl_u8(uint8x8_t a)

인접한 레인 사이의 더하기 연산을 하고 확장하여 저장한다. 결과 벡터는 피 연산자 레인 수의 절반이 되고, 레인의 크기는 2배로 커지지만, 결과 벡터와 피연산자 벡터의 크기는 동일하다.

r[0] = a[0]+a[1], r[1] = a[2]+a[3], r[2] = a[4]+a[5], r[3] = a[6]+a[7]


(14) Long 인접 레인 더하기 누계 함수

uint16x4_t r = vpadal_u8(uint16x4_t a, uint16x4_t b)

2개의 벡터를 이용하여 Long 인접 레인 더하기 연산을 하고, 다시 더하기 연산을 실행한다.

벡터 b에서 인접 레인과 더하기 연산을 수행한수, 그 결과를 벡터 a와 더한다. 벡터 a의 레인 크기는 변수 b의 레인 크기의 2배여야 하며, 벡터 b의 인접한 레인 더하기 연산이 먼저 실행되는 것에 주의해야 한다.


(15) 인접 레인 최댓값과 최솟값 함수

uint16x4_t r = vpmax_u16(uint16x4_t a, uint16x4_t b)

인접 레인 사이의 최댓값을 저장할수 있다.

  a[0] 와 a[1] 의 레인 최댓값을 r[0]에 저장 

  a[2] 와 a[3] 의 레인 최댓값을 r[1]에 저장

  b[0] 와 b[1] 의 레인 최댓값을 r[2]에 저장 

  b[2] 와 b[3] 의 레인 최댓값을 r[3]에 저장

'Programming > NEON' 카테고리의 다른 글

[NEON] 비트와 시프트 연산  (0) 2016.09.23
[NEON] 레인 설정과 조회  (0) 2016.09.23
[NEON] 함수 정리 (로드 함수, 저장 함수)  (0) 2016.09.23
[NEON] 벡터 자료형  (0) 2016.09.23
[NEON] 기본 개념  (0) 2016.09.23