티스토리 뷰
안녕하세요. Teus입니다.
이번 포스팅은 SIMD(Single Instrunction Multi Data)를 다룹니다.
C언어 Level의 Low수준을 다루게 됩니다.
1. SIMD란
SIMD는 Single Instrunction Multi Data의 약자 입니다.
글자로 알수 있듯, 하나의 명령어로 다수의 Data를 처리한는 방법 입니다.
아래 코드를 보실까요?
//일반계산
(int *)arr1 = (int *)malloc(sizeof(int)*8);
(int *)arr2 = (int *)malloc(sizeof(int)*8);
(int *)ret = (int *)malloc(sizeof(int)*8);
//8번의 정수연산을 위해서 8번의 반복문을 돌아야됨
for(int i=0; i<8; i++) {
ret[i] = arr1[i] + arr2[i];
}
//simd계산
int* arr1_ori = (int*)_aligned_malloc(sizeof(int) * 8, 32);
int* arr2_ori = (int*)_aligned_malloc(sizeof(int) * 8, 32);
__m256i arr1 = _mm256_load_si256((__m256i*)arr1_ori);
__m256i arr2 = _mm256_load_si256((__m256i*)arr2_ori);
//하나의 명령어로 8개의 정수 덧셈을 처리할수 있음
__m256i ret = _mm256_add_epi32(arr1, arr2);
여기서 앵 이거 걍 함수로 한줄처리하면 되는거 아뇨?🤔
생각했으면 좀더 공부하셔야 합니다.
위 __mm256_add_epi32
라고 하는 함수는 CPU의 명령어 수준에서 다수의 레지스터를 사용해서, CPU의 Cycle동안 더 많은 계산을 할 수 있게 준비되어있는 함수 입니다.
2. DataTypes
AVX/AVX2 명령어의 경우 아래처럼 DataType이 나뉘게 됩니다.
Data Type | Description |
---|---|
__m128 | 128-bit vector containing 4 floats |
__m128d | 128-bit vector containing 2 doubles |
__m128i | 128-bit vector containing integers |
__m256 | 256-bit vector containing 8 floats |
__m256d | 256-bit vector containing 4 doubles |
__m256i | 256-bit vector containing integers |
float = 32bit 실수
double = 64bit 실수
integer = 32bit or 64bit 정수
그래서 128 bit/32 bit per float => 4floats가 되는거죠
이때 128은 레지스터의 bit을 의미하며
레지스터의 bit이 증가할 수록 한번에 처리할 수 있는 Data의 양도 증가하게 됩니다.
(128bit SIMD는 한번에 128 bit의 데이터를 처리할 수 있는 것을 의미합니다)
3. DataFunc
이런 AVX DataType변수의 경우 단순한 연산을 하는것이 아니라
Cpu제조사에서 준비된 Intrinsic Instrunction을 사용하게 됩니다.
이 Nameing 체계를 보면 아래와 같습니다._mm<bit_width>_<name>_<data_type>
bit_widht : 128, 256, 512
name : 어떤연산을 할 것인지
data_type : 연산하는 Data의 type이 어느것인지
그래서 예를들어서
"256bit 레지스터를 가지고, 32bit정수의 더하기 연산을 하겠다" 하면
_mm256_add_epi32
같은 연산자를 사용하게 됩니다.
이때 뒤에 datatype의 경우 현재 epi32라고 나와있는데
이는 32bit signed Integer라고 가정하고 연산한다
를 의미합니다.
Data Type | Description |
---|---|
ps | vectors contain floats (ps stands for packed single-precision) |
pd | vectors contain doubles (pd stands for packed double-precision) |
epi8/epi16/epi32/epi64 | vectors contain 8-bit/16-bit/32-bit/64-bit signed integers |
epu8/epu16/epu32/epu64 | vectors contain 8-bit/16-bit/32-bit/64-bit unsigned integers |
si128/si256 | unspecified 128-bit vector or 256-bit vector |
m128/m128i/m128d/m256/m256i/m256d | identifies input vector types when they're different than the type of the returned vector |
4. 간단한 예시
AVX는 기본적으로 <imminstrin.h>
헤더파일을 통해서 함수를 제공합니다.
이제 두개의 Array를 만들고, 이 Array의 덧샘을 하는 간단한 코드를 보겠습니다.
#include <immintrin.h>
#include <stdio.h>
int main() {
//float으로 구성된 data array생성
__m256 arr1 = _mm256_set_ps(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0);
__m256 arr2 = _mm256_set_ps(1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0);
//AVX 연산을 사용해서 data를 연산
__m256 result = _mm256_add_ps(arr1, arr2);
//pointer로 typecasting후 일반적인 포인터처럼 사용
float* f = (float*)&result;
return 0;
}
3_1. DataFunc(scalar value로 초기화 하는 함수)
Function | Description |
---|---|
_mm256_setzero_ps/pd | Returns a floating-point vector filled with zeros |
_mm256_setzero_si256 | Returns an integer vector whose bytes are set to zero |
_mm256_set1_ps/pd | Fill a vector with a floating-point value |
_mm256_set1_epi8/16/32/64 | Fill a vector with an integer |
_mm256_set_ps/pd | Initialize a vector with eight floats (ps) or four doubles (pd) |
_mm256_set_epi8/16/32/64 | Initialize a vector with integers |
_mm256_set_m128/128d/128i | Initialize a 256-bit vector with two 128-bit vectors |
_mm256_setr_ps/pd | Initialize a vector with eight floats (ps) or four doubles (pd) in reverse order |
_mm256_setr_epi8/16/32/64 | Initialize a vector with integers in reverse order |
위 함수들을 사용하면
직접 value값을 전달하고, 그 값을 통해서 새로운 simd용 datatype의 변수를 생성할 수 있습니다.
#include <stdio.h>
#include <stdlib.h>
#include <xmmintrin.h>
#include <immintrin.h>
int data_num = 1000000;
int thread_cnt = 8;
int main() {
__m256d dbl_vector = _mm256_set1_pd(0.648);
double* my_vec = (double*)&dbl_vector;
for (int i = 0; i < 32; i++) {
printf("idx : %d, value = %f\n", i, my_vec[i]);
}
}
그러면 생성된 type을 통해서 일부 Array의 값이 초기화된것을 확인할 수가 있습니다.
3_2. DataFunc(Memory로 Data를 Load 하는 함수)
Data Type | Description |
---|---|
_mm256_load_ps/pd | Loads a floating-point vector from an aligned memory address |
_mm256_load_si256 | Loads an integer vector from an aligned memory address |
_mm256_loadu_ps/pd | Loads a floating-point vector from an unaligned memory address |
_mm256_loadu_si256 | Loads an integer vector from an |
_mm_maskload_ps/pd, _mm256_maskload_ps/pd | Load portions of a 128-bit/256-bit floating-point vector according to a mask |
_mm_maskload_epi32/64, _mm256_maskload_epi32/64 | Load portions of a 128-bit/256-bit integer vector according to a mask |
위 경우는 aligned된 상태의 memory를 이용해 simd연산을 하는 함수들 입니다.
aligned_malloc
이라는 함수를 사용하면 메모리는 일정 간격을 띄우면서 할당받을 수가 있습니다.
이때 aligned_malloc(sizeof(int)*8, 32) 라고 하면
아래와 같이 8개의 정수 크기만큼을 사용할 공간을 받으면서, Data는 32byte씩 떨어져 있는것을 보장받을 수가 있습니다.
다수의 Data를 빠르게 Load해야하는 SIMD연산에서 aligned 여부에 따라 성능이 차이나게 되는 것으로 알려져 있습니다.
그래서 기존에 할당받은 Memory가 aligned면 load를, aligned가 되어있지 않으면 loadu 함수를 사용합니다.
#include <stdio.h>
#include <stdlib.h>
#include <xmmintrin.h>
#include <immintrin.h>
int data_num = 1000000;
int thread_cnt = 8;
int main() {
double data_cnt = 8;
double* vec = (double*)_aligned_malloc(sizeof(double) * data_cnt, 32);
for (int i = 0; i < data_cnt; i++) {
vec[i] = 22;
}
__m256d simd_vec = _mm256_load_pd(vec);
for (int i = 0; i < 32; i++) {
printf("idx : %d, value = %f\n", i, vec[i]);
}
_aligned_free(vec);
}
위처럼 mem을 할당받고, 해당 mem은 typecasting혹은 위 simd 함수를 사용해서 simd를 위한 data type으로 만들수가 있습니다.
그 결과를 보시면 기존 memory값이 정삭적으로 출력되는것을 볼 수가 있습니다.
'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 튜토리얼3. mul/divide연산 (0) | 2024.04.24 |
AVX 튜토리얼2. Add/Sub연산 (0) | 2024.04.24 |
- Total
- Today
- Yesterday
- 병렬처리
- stack
- 이분탐색
- 자료구조
- SIMD
- git
- 동적계획법
- Search알고리즘
- Sort알고리즘
- 분할정복
- AVX
- hash
- 컴퓨터그래픽스
- GDC
- 코딩테스트
- 완전탐색 알고리즘
- heap
- Python
- 알고리즘
- Greedy알고리즘
- 프로그래머스
- C++
- prime number
- 사칙연산
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |