티스토리 뷰
안녕하세요. Teus입니다.
이번 포스팅은 SIMD(Single Instrunction Multi Data)를 다룹니다.
이번에는 AVX2의 기본 연산 중 쓸만한 함수 중 add/sub연산을 정리합니다.
1. Mul
Mul 연산도 add나 sub과 유사하게 사용할 수 있으면 좋겠지만
mul같은 경우 bit단위 연산을 고려해야 합니다.
TMI
32bit과 32bit을 연산할 경우 최대 64bit의 연산 결과가 발생합니다. 이러한 문제점이 있기 때문에 Multiply를 진행 할때는 단순하게 연산하는것이 아니라, 64bit의 상위 32bit을 쓰는 등과같이 bit단위로 선택적으로 data를 취득해서 사용하게 되어있습니다.
Data Type | Description |
---|---|
_mm256_mul_ps/pd | Multiply two floating-point vectors |
_mm256_mul_epi32/epu32 | Multiply the lowest four elements of vectors containing 32-bit integers |
_mm256_mullo_epi16/32 | Multiply integers and store low halves |
_mm256_mulhi_epi16/epu16 | Multiply integers and store high halves |
_mm256_mulhrs_epi16 | Multiply 16-bit elements to form 32-bit elements |
_mm256_div_ps/pd | Divide two floating-point vectors |
이때 _mm256_mul_epi32
를 사용해보면, 원하는것과 다른 결과가 나오는것을 확인할 수 있습니다.
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <xmmintrin.h>
#include <immintrin.h >
int data_num = 1000000;
int thread_cnt = 8;
int main() {
__m256i arr1 = _mm256_setr_epi32(2, 4, 6, 8, 10, 12, 14, 16);
__m256i arr2 = _mm256_setr_epi32(1, 3, 5, 7, 9, 11, 13, 15);
__m256i ret = _mm256_mul_epi32(arr1, arr2);
int* my_ret = (int*)&ret;
int* _arr1 = (int*)&arr1;
int* _arr2 = (int*)&arr2;
for (int i = 0; i < 8; i++) {
printf("idx : %d, arr1[%d] = %d, arr2[%d] = %d\n", i, i, _arr1[i], i, _arr2[i]);
}
for (int i = 0; i < 16; i++) {
printf("idx : %d, i : %d\n", i, my_ret[i]);
}
}
잘못 나온것은 아니고, mul_epi32는 아래처럼 정의되어 있습니다.
_mm256_mul_epi32
양쪽 Vector에서 64bit중 lower 32bit을 취하고, 이 값을 곱하는 방식으로 사용 됩니다.
그렇기 때문에 결과값을 제대로 확인할라면 코드를 아래처럼 일부 수정해야 합니다.
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <xmmintrin.h>
#include <immintrin.h >
int data_num = 1000000;
int thread_cnt = 8;
int main() {
__m256i arr1 = _mm256_setr_epi32(2, 4, 6, 8, 10, 12, 14, 16);
__m256i arr2 = _mm256_setr_epi32(1, 3, 5, 7, 9, 11, 13, 15);
__m256i ret = _mm256_mul_epi32(arr1, arr2);
//int * my_ret = (int*)&ret;
long long* my_ret = (long long*)&ret;
int* _arr1 = (int*)&arr1;
int* _arr2 = (int*)&arr2;
for (int i = 0; i < 8; i++) {
printf("idx : %d, arr1[%d] = %d, arr2[%d] = %d\n", i, i, _arr1[i], i, _arr2[i]);
}
for (int i = 0; i < 16; i++) {
printf("idx : %d, i : %d\n", i, my_ret[i]);
}
}
이렇게 해야지 정상적으로 결과를 얻어올 수 있습니다.
하지만 만족스럽지 않습니다.
우리가 원하는것은 ElementWise Multiply를 기대했지만
위 결과는 일부값만 곱하기를 한 것을 볼 수 있습니다.
이때 이제 mullo
와mulho
를 사용하게 됩니다.
_mm256_mulhi_epi16
: 두 Vector의 16bit 정수를 곱해서 32bit 정수를 만들고, 여기서 낮은 16bit 정수만 사용합니다.
_mm256_mulhi_epi16
: 위와 같은데, 높은 자리의 16bit을 사용합니다.
그래서 위 그림에서
_mm256_mul_epi8
을 사용하면 [011000101000000]에 해당하는 25216
_mm256_mullo_epi8
을 사용하면 [10000000]에 해당하는 128
_mm256_mulhi_epi8
을 사용하면 [01100010]에 해당하는 98
의 결과를 얻게 됩니다.
_mm256_mulhrs_epi16의 경우 설명만 보면 "와 이것만 쓰면 되지 딴거 왜씀?🤔" 할 수 있습니다. 이게 16bit정수의 곱셈 결과를 16bit 정수로 받는것 맞는데, 16bit정수의 곱셈 결과인 32bit정수에서 MSB(Most Significant Bit) 18개를 받고, 여기다 1을 더한다음 이중 16bit을 저장하는 방식이라, 우리 컴공과 엉아들이 필요하다고 생각해서 준비한 것으로는 보이는데.... 저는 어떻게 써야할지 감이 안옵니다.
_mm256__mulhrs_epi16
그 이외에 dvide는 계산 결과가 원본 숫자보다 bit수가 커질일이 없기 때문에, _mm256_add_ps/pd와 동일하게 사용하면 됩니다.
'C언어 잡기술 > SIMD(AVX)' 카테고리의 다른 글
AVX 튜토리얼6. 포인터 사용하기 (0) | 2024.04.24 |
---|---|
AVX 튜토리얼5. Permuting/Shuffling연산 (0) | 2024.04.24 |
AVX 튜토리얼4. FMA연산 (0) | 2024.04.24 |
AVX 튜토리얼2. Add/Sub연산 (0) | 2024.04.24 |
AVX 튜토리얼1. DataType (0) | 2024.04.02 |
- Total
- Today
- Yesterday
- 사칙연산
- 알고리즘
- stack
- 컴퓨터그래픽스
- 병렬처리
- 이분탐색
- Greedy알고리즘
- 분할정복
- AVX
- 완전탐색 알고리즘
- git
- 자료구조
- C++
- 코딩테스트
- 동적계획법
- GDC
- hash
- 프로그래머스
- Search알고리즘
- SIMD
- Python
- prime number
- Sort알고리즘
- heap
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |