53
SSE 병병 병병병병병 병병병병 병병병병 ( 병병병 )

[0204 구경원] sse 병렬 프로그래밍

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: [0204 구경원] sse 병렬 프로그래밍

SSE 병렬 프로그래밍

데브루키돼지고기 ( 구경원 )

Page 2: [0204 구경원] sse 병렬 프로그래밍

SIMD.

• Single Instruction Multiple Data• CPU 에서 지원하는 일종의 명령어 셋 .• 한번의 연산으로 다수의 데이터를 처리할 수

있다 .

• SISD - Single Instruction Single Data• SIMD - Single Instruction Multiple Data• MISD - Multiple Instruction Single Data• MIMD - Multiple Instruction Multiple Data

Page 3: [0204 구경원] sse 병렬 프로그래밍

SIMD.

Input Data Output Data

SISD 명령어

Input Data Output Data

SIMD 명령어

Page 4: [0204 구경원] sse 병렬 프로그래밍

SIMD.

… … 11 10 v 8 7 6 5 4 3 2 1

4 3 2 1

8 7 6 5

• SISD

• SIMD 연산의 방향32 bit 32 bit 32 bit 32 bit

4 3 2 1

+ + + +

8 7 6 5

+ + + +

12 11 10 9

+ + + +

16 15 14 13

= = = =

40 36 32 28

4 = 136값의 합

Page 5: [0204 구경원] sse 병렬 프로그래밍

• 어셈블리 SIMD 명령어 • Intrinsic 함수• Vector Class

SIMD 구현 방법 .

SIMD 명령어 Intrinsic 함수 Vector Class

xmm0 __m128i Is16vec8

xmm1 __m128 Is32vec4 ~

~ __m128d F32vec4

xmm7 F64vec2

Page 6: [0204 구경원] sse 병렬 프로그래밍

SIMD 구현 방법 .

Page 7: [0204 구경원] sse 병렬 프로그래밍

• 프로젝트의 중요 부분 또는 병목 지점인가 .• SIMD 구조에 적합한가 .• 성능향상에 도움이 되어지는가 .• 정수형 , 실수형 인지 파악 .• 128bit 에 담을 수 있는 데이터 개수 고려 .• 제작기간 , 디버깅 테스트 기간 고려 .• 구현 도구 결정 .• SISD 와 SIMD 성능 비교 테스트 .

SIMD 구현 조건 .

Page 8: [0204 구경원] sse 병렬 프로그래밍

SSE.

• Streaming SIMD Extensions• XMM 128 비트 레지스터 8 개가 존재 .• 인텔이 1999 년 펜티엄 3 프로세서에 도입 .• FLOAT, POINT. 비교로직 등 다양한 연산

가능 .

Packing 사이즈 byte short integer

병렬 연산 개수 16 8 4

C 코드와 성능 차이 4~6 배 2 배 10~30%

Page 9: [0204 구경원] sse 병렬 프로그래밍

CPU 구현 프로세서 .

Page 10: [0204 구경원] sse 병렬 프로그래밍

IA-32 / 64 레지스터

Page 11: [0204 구경원] sse 병렬 프로그래밍

32 bit A3 32 bit A2 32 bit A1 32 bit A0

Scalar 덧셈 계산 +

32 bit B3 32 bit B2 32 bit B1 32 bit B0

=

32 bit A3 32 bit A2 32 bit A1 32 bit A0+B0

32 bit A3 32 bit A2 32 bit A1 32 bit A0

Packed 덧셈 계산 + + + +

32 bit B3 32 bit B2 32 bit B1 32 bit B0

= = = =

32 bit A3+B3 32 bit A2+B2 32 bit A1+B1 32 bit A0+B0

SIMD 연산 타입

Page 12: [0204 구경원] sse 병렬 프로그래밍

중요 어셈블리

• mov • add / sub• mul / div• inc / dec

• shl / shr• cmp / jp

Page 13: [0204 구경원] sse 병렬 프로그래밍

어셈블리 예제 .

__asm{

pushadmov eax, Amov ebx, Badd eax, ebxmov C, eaxpopad

}

Page 14: [0204 구경원] sse 병렬 프로그래밍

어셈블리 예제 .

__asm{

pushadmov ebx, 15mov eax, Amul ebxmov C, eaxpopad

}

Page 15: [0204 구경원] sse 병렬 프로그래밍

어셈블리 예제 .

__asm{

pushad

mov eax, 17cdq //32 bit 를 64 bit 로 확장//convert double word to quad word

mov ebx, Adiv ebxmov B, eax

mov C, edx

popad}

Page 16: [0204 구경원] sse 병렬 프로그래밍

어셈블리 예제 .

__asm{

pushad

mov eax, 0LOOP:

// 필요한 연산

inc eaxcmp eax, 1000jne LOOP

mov nValue, eaxpopad

}

Page 17: [0204 구경원] sse 병렬 프로그래밍

SIMD 명령어

• 정수형과 실수형 두 가지 병렬 연산 방식이 있다 .• Pack 형식에 따라 연산 방식이 달라진다 .• MMX 에서는 64bit 병렬 연산만 가능했지만 SSE

로 넘어오면서 128bit 병렬 연산이 가능해졌다 .• 구현코드가 CPU 에서 똑같이 동작한다 .• 디버깅이 어렵고 , 가독성이 안좋다 .

Page 18: [0204 구경원] sse 병렬 프로그래밍

명명법

P <SIMD_op> <suffix>

접미사 원 어 사이즈 의 미

S signed - 양수값을 의미하는 접미사로 사이즈를 의미하는 단어 앞에 오게 된다 .

U unsigned - +- 부호를 갖는 데이터형임을 의미한다 .

B Byte 8 bit 해당 데이터는 8 bit 정수 형 16 개를 연산할 수 있다 .

W Word 16 bit 해당 데이터는 16 bit 정수 형 8 개를 연산할 수 있다 .

D DoubleWord 32 bit 해당 데이터는 32 bit 정수 형 4 개를 연산할 수 있다 .

Q QuadWord 64 bit 해당 데이터는 64 bit 정수 형 2 개를 연산할 수 있다 .

Page 19: [0204 구경원] sse 병렬 프로그래밍

• 정렬된 메모리를 사용하는 것이 빠르다 .

• 정렬된 메모리• __declspec( align(16) ) int array[100];

• 정렬되지 않은 메모리• Int array[100]

메모리 align, unalign

Page 20: [0204 구경원] sse 병렬 프로그래밍

MOVDQU Move Unaligned Double Quad word

• 128bit 레지스트 또는 128bit 메모리에 정렬되지 않은 값을 읽어올 때 사용한다

16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit

16 Byte Unaligned Memory 8 7 6 5 4 3 2 1

MOVDQU 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit

xmm0 레지스터 8 7 6 5 4 3 2 1

Page 21: [0204 구경원] sse 병렬 프로그래밍

• 128bit 레지스터 또는 128bit 메모리에 정렬되어 있는 값을 읽어올 때 사용 한다 .

• 메모리가 정렬되어 있기 때문에 읽어오는 속도가 빠르다 .

MOVDQA Move aligned Double Quad word

16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit

16 Byte Aligned Memory 8 7 6 5 4 3 2 1

MOVDQA 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit

xmm0 레지스터 8 7 6 5 4 3 2 1

Page 22: [0204 구경원] sse 병렬 프로그래밍

논리연산

32 bit 32 bit 32 bit 32 bit

SourceA 1 1 0 0

SourceB 1 0 1 0

PAND 1 0 0 0

POR 1 1 1 0

PXOR 0 1 1 0

PANDN 0 0 1 0

Page 23: [0204 구경원] sse 병렬 프로그래밍

PADDD (Packed Add)

32 bit 32 bit 32 bit 32 bit

xmm0 4 3 2 1

paddd + + + +

xmm1 8 7 6 5

= = = =

xmm0 12 10 8 6

• 더하기 연산을 한다

Page 24: [0204 구경원] sse 병렬 프로그래밍

PADDD (Packed Add)

int IntArrayA[4] = { 1,2,3,4 };int IntArrayB[4] = { 5,6,7,8 };int IntResult[4] = {0};

__asm{

pushad

movdqu xmm0, IntArrayAmovdqu xmm1, IntArrayBpaddd xmm0, xmm1movdqu IntResult, xmm0

popademms

}

Page 25: [0204 구경원] sse 병렬 프로그래밍

… … 11 10 v 8 7 6 5 4 3 2 1

4 3 2 1

8 7 6 5

• SISD

• SIMD 연산의 방향32 bit 32 bit 32 bit 32 bit

4 3 2 1

+ + + +

8 7 6 5

+ + + +

12 11 10 9

+ + + +

16 15 14 13

= = = =

40 36 32 28

4 = 136값의 합

PADDD (Packed Add)

Page 26: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

short GetMaxValueC(const short *pShortArray,const int nSize) { short MaxValue = 0;

for(int i = 0; i< nSize ; i++){

if(pShortArray[i] > MaxValue )MaxValue = pShortArray[i];

}return MaxValue;

}

Page 27: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

const short* pShort = pShortArray;// 스택포인터변수로받아온다 .

int nLoopCount = (nSize / 8)*16;//8 배수개수를한번에계산

int nRemain = nSize % 8;//8 배수나머지영역은 C 로구현

short nMaxValue = 0;// 결과값을가지고있을메모리변수

Page 28: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기• __asm• {• pushad• mov eax, pShort• mov esi, 0•

• mov ecx, nLoopCount• pxor xmm1, xmm1• FINDLP:• movdqu xmm0, [eax+esi] .

• pmaxsw xmm1, xmm0•

• add esi, 16 .• cmp esi, ecx• jne FINDLP

• movdqu MaxValueArray, xmm1• popad• emms• }

Page 29: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기• for( unsigned char Count = 0; Count < 8 ; Count++)• {• if( MaxValueArray[Count] > nMaxValue )• nMaxValue = MaxValueArray[Count];• }•

• if( nRemain != 0)• {• for( int i = nSize-nRemain; i< nSize; i++)• {• if(pShortArray[i] > nMaxValue )• nMaxValue = pShortArray[i];• }• }

Page 30: [0204 구경원] sse 병렬 프로그래밍

GetLength()f = sqrtf(x_ * x_ + y_ * y_ + z_ * z_);

• FLOAT* p = &f; w_ = 0.0f;

• __asm {– mov ecx, p– mov esi, this – movups xmm0, [esi] – mulps xmm0, xmm0 – movaps xmm1, xmm0 – shufps xmm1, xmm1, 01001110b – addps xmm0, xmm1– movaps xmm1, xmm0 – shufps xmm1, xmm1, 00010001b– addps xmm0, xmm1– sqrtss xmm0, xmm0 – movss [ecx], xmm0

• }

Page 31: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

Page 32: [0204 구경원] sse 병렬 프로그래밍

• 코드의 분석이 쉽다 .• SIMD 명령어를 inline 함수로 구현하여 , 함수의

성능은 SIMD 명령어 셋과 차이가 별로 없다 .• Scalar 형 intrinsic 함수는 어셈블리 SIMD

명령어 보다 약간의 성능저하가 있을 수 있다 .• SIMD 명령어 보다 코드 작성에 편리하다 .

Intrinsic 함수

Page 33: [0204 구경원] sse 병렬 프로그래밍

명명법

_mm_<intrin_op>_<suffix>

문자 데이터 타입

s 32bit 실수형 (single-precision floating point)

d 64bit 실수형 (double-precision floating point)

i128 128bit signed 정수 (signed 128-bit integer)

i64 64bit signed 정수 (signed 64-bit integer)

u64 64bit unsigned 정수 (unsigned 64-bit integer)

i32 32bit signed 정수 (signed 32-bit integer)

u32 32bit unsigned 정수 (unsigned 32-bit integer)

i16 16bit signed 정수 (signed 16-bit integer)

u16 16bit unsigned 정수 (unsigned 16-bit integer)

i8 8bit signed 정수 (signed 8-bit integer)

u8 8bit unsigned 정수 (unsigned 8-bit integer)

Page 34: [0204 구경원] sse 병렬 프로그래밍

• 헤더 파일 include• - #include <xmmintrin.h> SSE

- #include <emmintrin.h> SSE2- #include <pmmintrin.h> SSE3- #include <smmintrin.h> <nmmintrin.h> SSE4

Intrinsic 함수

Page 35: [0204 구경원] sse 병렬 프로그래밍

• SIMD 연산을 위한 자료형으로 XMM 레지스트와 1 대 1 대응되는 구조체이다 .

__m128 자료형

___m128i 32 bit integer 32 bit integer 32 bit integer 32 bit integer

Page 36: [0204 구경원] sse 병렬 프로그래밍

Intrinsic LOAD. STORE

__m128i r = _mm_load_si128(__m128i const*p)

16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit

16Byte Align Memory p p[7] p[6] p[5] p[4] p[3] p[2] p[1] p[0]

__m128i r r[7] r[6] r[5] r[4] r[3] r[2] r[1] r[0]

void _mm_store_si128(__m128i *p, __m128i b)

16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit 16 bit

__m128i b b[7] b[6] b[5] b[4] b[3] b[2] b[1] b[0]

16Byte Align Memory p p[7] p[6] p[5] p[4] p[3] p[2] p[1] p[0]

Page 37: [0204 구경원] sse 병렬 프로그래밍

Intrinsic 함수

short Source[8] = {1,2,3,4,5,6,7,8};short Dest[8] = {0};

__m128i xmm0 = _mm_loadu_si128((__m128i*)Source);__m128i xmm1 = xmm0;

_mm_storeu_si128((__m128i*)Dest, xmm1);

Page 38: [0204 구경원] sse 병렬 프로그래밍

Intrinsic ADD. SUB

__m128i R = _mm_add_epi16(__m128i a, __m128i b)

__m128i a a7 a6 a5 a4 a3 a2 a1 a0

+ + + + + + + +

__m128i b b7 b6 b5 b4 b3 b2 b1 b0

= = = = = = = =

__m128i r a7+b7 a6+b6 a5+b5 a4+b4 a3+b3 a2+b2 a1+b1 a0+b0

__m128i R = _mm_sub_epi16(__m128i a, __m128i b)

__m128i a a7 a6 a5 a4 a3 a2 a1 a0

- - - - - - - -

__m128i b b7 b6 b5 b4 b3 b2 b1 b0

= = = = = = = =

__m128i r a7-b7 a6-b6 a5-b5 a4-b4 a3-b3 a2-b2 a1-b1 a0-b0

Page 39: [0204 구경원] sse 병렬 프로그래밍

Intrinsic MUL

__m128i R = _mm_mullo_epi16(__m128i a, __m128i b)

__m128i a a7 a6 a5 a4 a3 a2 a1 a0

* * * * * * * *

__m128i b b7 b6 b5 b4 b3 b2 b1 b0

= = = = = = = =

__m128i r a7*b7 a6*b6 a5*b5 a4*b4 a3*b3 a2*b2 a1*b1 a0*b0

Page 40: [0204 구경원] sse 병렬 프로그래밍

Intrinsic MAX. MIN

__m128i R = _mm_max_epi16(__m128i a, __m128i b)

__m128i a a7 a6 a5 a4 a3 a2 a1 a0

max max max max max max max max

__m128i b b7 b6 b5 b4 b3 b2 b1 b0

= = = = = = = =

__m128i r max(a7,b7) max(a6,b6) max(a5,b5) max(a4,b4) max(a3,b3) max(a2,b2) max(a1,b1) max(a0,b0)

__m128i R = _mm_min_epi16(__m128i a, __m128i b)__m128i a a7 a6 a5 a4 a3 a2 a1 a0

min min min min min min min min

__m128i b b7 b6 b5 b4 b3 b2 b1 b0

= = = = = = = =

__m128i rmin(a7,b7

)min(a6,b6

)min(a5,b5

)min(a4,b4

)min(a3,b3) min(a2,b2) min(a1,b1) min(a0,b0)

Page 41: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

const short* pShort = pShortArray;int nRemain = nSize % 8;short nMaxValue = 0;short MaxValueArray[8] ={0};

__m128i XMMCurrentValue;__m128i XMMMaxValue;

for(unsigned int Index =0 ; Index < nSize; Index+=8){

XMMCurrentValue = _mm_loadu_si128((__m128i*)(pShortArray+Index)); //16byte 씩읽어온다 .

XMMMaxValue = _mm_max_epi16(XMMMaxValue, XMMCurrent-Value); //16byte 씩더한다 .}

Page 42: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

Page 43: [0204 구경원] sse 병렬 프로그래밍

Vector 클래스

• Intrinsic 데이터형 또는 함수를 클래스화시킨 라이브러리 .

• Intrinsic 를 이용할 때 보다 직관적이고 사용이 편리하다 .

• 연산자 오버로딩으로 만들어져 있다 .

Page 44: [0204 구경원] sse 병렬 프로그래밍

• 명명법– <type><signedness><bits>vec<elements>

Iu32vec432bit unsigned int 형 정수를 4 개 담고 있는 vector 클래스 .

Fs16vec88bit signed short 형 실수를 8 개 담고 있는 vector 클래스 .

Vector 클래스

Page 45: [0204 구경원] sse 병렬 프로그래밍

Vector 클래스class 부호 pack 데이터 타입 pack 사이즈 pack 개수 해더 파일

I128vec1 unspecified __m128i 128 1 dvec.h

I64vec2 unspecified __int64 64 2 dvec.h

Is64vec2 signed __int64 64 2 dvec.h

Iu64vec2 unsigned __int64 64 2 dvec.h

I32vec4 unspecified int 32 4 dvec.h

Is32vec4 signed int 32 4 dvec.h

Iu32vec4 unsigned int 32 4 dvec.h

I16vec8 unspecified short 16 8 dvec.h

Is16vec8 signed short 16 8 dvec.h

Iu16vec8 unsigned short 16 8 dvec.h

I8vec16 unspecified char 8 16 dvec.h

Is8vec16 signed char 8 16 dvec.h

Iu8vec16 unsigned char 8 16 dvec.h

Page 46: [0204 구경원] sse 병렬 프로그래밍

Vector 클래스

__asm{movaps xmm0, amovaps xmm1, baddps xmm0, xmm1movaps c, xmm0}

#include < xmmintrin.h >__m128 a, b, c;c = _mm_add_ps( a, b);

#include <fvec.h>F32vec4 A, B, CC = A + B;

Page 47: [0204 구경원] sse 병렬 프로그래밍

Vector 클래스 읽고 . 쓰기__declspec(align(16)) short A[8] = {1,2,3,4,5,6,7,8};__declspec(align(16)) short R[8] = {0};

Is16vec8 Vector(1,2,3,4,5,6,7,8); // 역순으로 8 부터 입력

_mm_store_si128((__m128i *)R, Vector); //intrinsic 함수로 쓰기

printf("Store : %d, %d, %d, %d, %d, %d, %d, %d\n",R[0],R[1],R[2],R[3],R[4],R[5],R[6],R[7]);

Is16vec8 *a = (Is16vec8 *)A; // 포인터 캐스팅으로 바로 읽기

Is16vec8 *r = (Is16vec8 *)R;

*r = *a; // 대입 연산

printf("Store : %d, %d, %d, %d, %d, %d, %d, %d\n",R[0],R[1],R[2],R[3],R[4],R[5],R[6],R[7]);

return 0;

Page 48: [0204 구경원] sse 병렬 프로그래밍

Vector 클래스 사칙연산

Is16vec8 A;Is16vec8 B;Is16vec8 R;R = A + B; // 덧셈 연산R += A;R = A - B; // 뺄셈 연산R -= A;R = A * B; // 곱셈 연산R *= A;R = mul_high( A, B ); // 곱셈 상위 bitR = mul_add( A, B); // 곱 합 연산

Page 49: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

const short* pShort = pShortArray;int nRemain = nSize % 8;short nMaxValue = 0;short MaxValueArray[8] ={0};

Is16vec8* XMMCurrentValue;Is16vec8 XMMMaxValue;

for(unsigned int Index =0 ; Index < nSize; Index+=8){

XMMCurrentValue = (Is16vec8*)(pShortArray+Index);XMMMaxValue = simd_max(XMMMaxValue,* XMMCurrent-

Value);}

Page 50: [0204 구경원] sse 병렬 프로그래밍

최대값 구하기

Page 51: [0204 구경원] sse 병렬 프로그래밍

마무리

- SSE 를 사용하면 C/C++ 보다 빠른 성능 향상을 볼 수 있다 .- 멀티코어 시대에 SSE 의 사용은 미래를 대비하는 일이다 .- 아직까지 가독성의 문제가 남아있다 .- 반복되는 계산량이 많은 부분에서 많은 이득을 볼 수 있다 .- 캐쉬라인 정렬이 되어있어야 효율적이다 .

Page 52: [0204 구경원] sse 병렬 프로그래밍

Q&A

Page 53: [0204 구경원] sse 병렬 프로그래밍

END