티스토리 뷰

728x90
반응형

안녕하세요. 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]);
    }
}

image.png


잘못 나온것은 아니고, mul_epi32는 아래처럼 정의되어 있습니다.
_mm256_mul_epi32

image.png

양쪽 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]);
    }
}

image.png


이렇게 해야지 정상적으로 결과를 얻어올 수 있습니다.

 

하지만 만족스럽지 않습니다.

 

우리가 원하는것은 ElementWise Multiply를 기대했지만

 

위 결과는 일부값만 곱하기를 한 것을 볼 수 있습니다.

 

이때 이제 mullomulho를 사용하게 됩니다.

 

_mm256_mulhi_epi16 : 두 Vector의 16bit 정수를 곱해서 32bit 정수를 만들고, 여기서 낮은 16bit 정수만 사용합니다.

_mm256_mulhi_epi16 : 위와 같은데, 높은 자리의 16bit을 사용합니다.

image.png


그래서 위 그림에서

 

_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

image.png

그 이외에 dvide는 계산 결과가 원본 숫자보다 bit수가 커질일이 없기 때문에, _mm256_add_ps/pd와 동일하게 사용하면 됩니다.

728x90
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함