SlideShare a Scribd company logo
1 of 42
SSE 를 이용한 최적화와 실제 사용 예 이권일 EA Seoul Studio (BFO)
발표 대상 C/C++ 프로그래머 H/W 및 최적화에 관심 있는 자 GPGPU 를 준비하는 자
SSE (SIMD Streaming Extension) 1999년 펜티엄3 에 처음 포함된 확장 기능 Float Point 및 비교 로직 등 다양한 연산 SSE 전용 128bit XMM 레지스터 8개 추가 MMX 와 달리 거의 모든 기능이 구현됨
x86/x64 레지스터
SIMD 연산 일반연산 1.0 2.0 3.0 4.0 1.0 5.0 6.0 7.0 8.0 5.0 6.0 8.0 10.0 12.0 6.0
__m128자료형 typedef union __declspec(intrin_type) _CRT_ALIGN(16) __m128 { float               		m128_f32[4]; unsigned __int64		m128_u64[2]; __int8              		m128_i8[16]; __int16             		m128_i16[8]; __int32             		m128_i32[4]; __int64             		m128_i64[2]; unsigned __int8     	m128_u8[16]; unsigned __int16    	m128_u16[8]; unsigned __int32    	m128_u32[4];  } __m128; ,[object Object]
SSE 2 부터 새로이 추가된 __int64 와 double 을 지원하기 위한 __m128i, __m128d 자료형도 있음
명령어에 따라 2,4,8,16 SIMD 연산이 수행될 수 있음. 구조체에는 어떤 데이터가 들어 있는지 알 수 없음,[object Object]
편하게 코딩하기 // 산술 연산자 __forceinline__m128operator+(__m128 l, __m128 r)	{ return_mm_add_ps(l,r); } __forceinline__m128operator-(__m128 l, __m128 r)	{ return_mm_sub_ps(l,r); } __forceinline__m128operator*(__m128 l, __m128 r)	{ return_mm_mul_ps(l,r); } __forceinline__m128operator/(__m128 l, __m128 r)	{ return_mm_div_ps(l,r); } __forceinline__m128operator+(__m128 l, float r)		{ return_mm_add_ps(l,_mm_set1_ps(r)); } __forceinline__m128operator-(__m128 l, float r)		{ return_mm_sub_ps(l, _mm_set1_ps(r)); } __forceinline__m128operator*(__m128 l, float r)		{ return_mm_mul_ps(l, _mm_set1_ps(r)); } __forceinline__m128operator/(__m128 l, float r)		{ return_mm_div_ps(l, _mm_set1_ps(r)); } // 논리 연산자 __forceinline__m128operator&(__m128 l, __m128 r)	{ return_mm_and_ps(l,r); } __forceinline__m128operator|(__m128 l, __m128 r)	{ return_mm_or_ps(l,r); } // 비교 연산자 __forceinline__m128operator<(__m128 l, __m128 r)	{ return_mm_cmplt_ps(l,r); } __forceinline__m128operator>(__m128 l, __m128 r)	{ return_mm_cmpgt_ps(l,r); } __forceinline__m128operator<=(__m128 l, __m128 r)	{ return_mm_cmple_ps(l,r); } __forceinline__m128operator>=(__m128 l, __m128 r)	{ return_mm_cmpge_ps(l,r); } __forceinline__m128operator!=(__m128 l, __m128 r)	{ return_mm_cmpneq_ps(l,r); } __forceinline__m128operator==(__m128 l, __m128 r)	{ return_mm_cmpeq_ps(l,r); }
SIMD 정말 4배 빠른가요? // C 버젼  for(size_ti=0; i<count;++i) { 	b[i] = a[i] + a[i]; } -> 실행 시간 49.267 ms // Compiler Intrinsic 버젼 for(size_ti=0; i<count/4;++i) { 	b4[i] = a4[i] + a4[i]; } -> 실행 시간 47.927 ms
메모리 병목!! a[0] b[0] + a[1] b[1] + a[2] B[2] + a[3] b[3] + a[4] + a[5] + a[0] b[0] a[1] b[1] a[2] b[2] a[3] b[3] a[4] a[5] a[6] a[7] + + + + + + +
연산량을 늘리자! sinf() // sin(a) = a – (a^3)/3! + (a^5)/5! – (a^7)/7! … float req_3f = 1.0f / (3.0*2.0*1.0); float req_5f = 1.0f / (5.0*4.0*3.0*2.0*1.0); float req_7f = 1.0f / (7.0*6.0*5.0*4.0*3.0*2.0*1.0); for(size_ti=0; i<count; ++i) { 	b[i] = a[i]  		- a[i]*a[i]*a[i]*req_3f  		+ a[i]*a[i]*a[i]*a[i]*a[i]*req_5f  		- a[i]*a[i]*a[i]*a[i]*a[i]*a[i]*a[i]*req_7f; } -> 실행 시간 111. ms
C 언어의 연산 병목 a[0] b[0] + a[1] b[1] + a[2] b[2] + a[3] b[3] + a[4] + a[0] a[1] a[2] a[3] a[4] b[0] b[1] b[2] b[3] + + + + +
SSE 버젼의 sinf()  // sin(a) = a – (a^3)/3! + (a^5)/5! – (a^7)/7! … __m128 req_3f4 = _mm_set1_ps(req_3f); __m128 req_5f4 = _mm_set1_ps(req_5f); __m128 req_7f4 = _mm_set1_ps(req_7f); for(size_ti=0; i<count/4; ++i) { 	b4[i] = a4[i]  		- a4[i]*a4[i]*a4[i]*req_3f4  		+ a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*req_5f4  		- a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*req_7f4; } -> 실행 시간 48.939 ms
SSE는 아직도 메모리 병목!!  a[0,1,2,3] + b[0,1,2,3] a[4,5,6,7] + b[4,5,6,7] a[8,9,10,11] + b[8,9,10,11] a[12,13,14,15] + b[12,13,14,15] a[16,17,18,19] a[0,1,2,3] b[0,1,2,3] a[4,5,6,7] b[4,5,6,7] a[8,9,10,11] b[8,9,10,11] a[12,13,14,15] b[12,13,14,15] a[16,17,18,19] + + + +
a+a과 sin() 연산 시간이 같다 ? ,[object Object]
SSE 에서 a4[i] + b4[i] 를 구성하는데 6 명령어로 실행되었고 sin() 은 29 명령어로 실행,[object Object]
그러나 컴파일러는 Vectorization을 잘 못한다.,[object Object]
몇배나 빠르다고요?
_mm_stream_ps() // C 버젼  for(size_ti=0; i<count;++i) { 	b[i] = a[i] + a[i]; } -> 실행 시간 49.267 ms // a+a stream 버젼 for(size_ti=0; i<count/4;++i) { _mm_stream_ps((float*)(b4+i), _mm_add_ps(a4[i], a4[i])); } -> 실행 시간 30.114 ms
CPU _mm_stream_ps() 의 작동 Excution Unit L1 Cache L2 Cache WC Buffer Memory BUS Memory
_mm_stream_ps() 는 빠르다 !! Move Aligned Four Packed Single-FP Non Temporal ,[object Object]
쓰기 순서를 보장하지 않으므로 쓰고 바로 읽으면 안됨,[object Object]
Stream 을 추가한 그래프 !!
같은 시간에 더 많은 일을 합시다!!     float Read + Write 시간 : 2.896 ns __m128 Read + Write 시간 : 11.214 ns __m128 Read + Stream 시간 : 6.977 ns
SSE 프로그래밍 메모리 접근 시간이 길어지고 연산시간이 짧아짐에 따라 더 많은 계산을 할 수 있다. 요즘 CPU는 Out-of-Order 로 인해 대부분 비동기 실행을 한다. 적극 이용하자. 병렬화와 병목 문제는 GPGPU 연산에도 동일하게 적용된다. 미래를 대비하자.!!
SSE 를 적용한 예제들
SSE 를 사용한 CPU Skinning Vertex : 1024 * 1024  Bone : 200 4 weight per vertex + normal + tangent SSE 컴파일 옵션이 켜진 C,  SSE최적화 스키닝 없는 C 루프 복사, SSE 루프 복사, memcpy()
C Skinning Code //  Optimized C Version  D3DXMATRIX m = 	b[in->index[0]] * in->blend[0]  			+ b[in->index[1]] * in->blend[1]  			+ b[in->index[2]] * in->blend[2]  			+ b[in->index[3]] * in->blend[3]; out->position.x = in->position.x*m._11 + in->position.y*m._21 + in->position.z*m._31 + m._41; out->position.y = in->position.x*m._12 + in->position.y*m._22 + in->position.z*m._32 + m._42; out->position.z = in->position.x*m._13 + in->position.y*m._23 + in->position.z*m._33 + m._43; out->normal.x = in->normal.x*m._11 + in->normal.y*m._21 + in->normal.z*m._31; out->normal.y = in->normal.x*m._12 + in->normal.y*m._22 + in->normal.z*m._32; out->normal.z = in->normal.x*m._13 + in->normal.y*m._23 + in->normal.z*m._33; out->tangent.x = in->tangent.x*m._11 + in->tangent.y*m._21 + in->tangent.z*m._31; out->tangent.y = in->tangent.x*m._12 + in->tangent.y*m._22 + in->tangent.z*m._32; out->tangent.z = in->tangent.x*m._13 + in->tangent.y*m._23 + in->tangent.z*m._33;
SSE Skinning Code // SSE Code __m128 b0 = _mm_set_ps1(in->blend[0]); __m128 b1 = _mm_set_ps1(in->blend[1]); __m128 b2 = _mm_set_ps1(in->blend[2]); __m128 b3 = _mm_set_ps1(in->blend[3]); __m128* m[4] = { 	(__m128*)( matrix+in->index[0] ),  			(__m128*)( matrix+in->index[1] ),  			(__m128*)( matrix+in->index[2] ),  			(__m128*)( matrix+in->index[3] ) 	}; __m128 m0 = m[0][0]*b0 + m[1][0]*b1 + m[2][0]*b2 + m[3][0]*b3; __m128 m1 = m[0][1]*b0 + m[1][1]*b1 + m[2][1]*b2 + m[3][1]*b3; __m128 m2 = m[0][2]*b0 + m[1][2]*b1 + m[2][2]*b2 + m[3][2]*b3; __m128 m3 = m[0][3]*b0 + m[1][3]*b1 + m[2][3]*b2 + m[3][3]*b3; _mm_stream_ps( out->position, m0*in->position.x+m1*in->position.y+m2*in->position.z+m3 ); _mm_stream_ps( out->normal, m0*in->normal.x+m1*in->normal.y+m2*in->normal.z ); _mm_stream_ps( out->tangent, m0*in->tangent.x+m1*in->tangent.y+m2*in->tangent.z );
SSE Skinning 결과 memcpy() 시간의 80% 로 스키닝을 할 수 있다. 파티클, UI 등에 유용하게 사용할 수있다. Dynamic VB 를 쓰는 동안 계산을 추가로 할 수 있다.
SSE를 사용한 KdTree ,[object Object]
Deep-Narrow Tree 를 만들어야 효율이 좋아지므로 노드가 무척 많아진다.
Tree Node 방문이 전체 처리 시간의 90% 을 차지한다.,[object Object]
kDTree Packet Traverse
KdTree테스트 결과
Scaleform과 SSE Flash 파일을 3D 가속을 받으며 실행 가능하도록 만들어진 라이브러리 Direct3D/OpenGL 및 다양한 렌더링 라이브러리 지원 현재 프로젝트의 UI 제작에 사용 209개 파일 65147 Line 의 Acton Script 와 DXT5 79MB UI 이미지
Scaleform 3.1 의 문제점 복잡한 swf들을 다수 사용할 경우 CPU 사용률이 상당히 높다. 높은 자유도가 GPU에 최적화 되기 어려운 UI 를 만들게 한다. GRendererD3D9 은예제 코드에 가깝고 개발시 H/W 특성이 고려되지 않았다.
Scaleform개선 방향 Client GFx Client GFx GFxQueue Direct3D Direct3D GFxMoveView::Advance() GFxMoveView::Advance() SceneMgr::DrawScene() GFxMoveView::DisplayMT() SceneMgr::DrawScene() GFxQueue::DrawPrim() GFxMoveView::Display() GFxQueue::Flush() ID3DDevice::DrawPrim() 5~15ms/frame ID3DDevice::DrawPrim()
GFxQueue의 Batch 합치기 기능 Batch 합치기를 하기 위해 Vertex 를 Queue 에 넣을때 Transform (TnL) 을 미리 처리 Render State, Texture State 를 체크해서 중복된 렌더링 재설정을 방지 Scene 에서 벗어난 Shape 들안그리는 기능 추가 CPU로 대체된 VertexShader는 삭제, Pixel Shader도 Batch 합치기를 위해 수정
Transform 코드 caseVS_XY16iCF32: { 	XY16iCF32_VERTEX* input = (XY16iCF32_VERTEX*)src + start; for(UINT i=0; i<count; ++i){ 		//output->pos.x = g_x + (input->x * vertexShaderConstant[0].x + input->y * vertexShaderConstant[1].x + vertexShaderConstant[2].x) * g_width; 		//output->pos.y = g_y - (input->x * vertexShaderConstant[0].y + input->y * vertexShaderConstant[1].y + vertexShaderConstant[2].y) * g_height; 		//output->pos.z = 1; 		//output->pos.w = 1; 		//output->color = FlipColor(input->color); 		//output->factor = FlipColor(input->factor); 		//output->tc0.x = input->x * vertexShaderConstant[3].x + input->y * vertexShaderConstant[4].x + vertexShaderConstant[5].x; 		//output->tc0.y = input->x * vertexShaderConstant[3].y + input->y * vertexShaderConstant[4].y + vertexShaderConstant[5].y; 		//aabb.AddPoint(output->pos); __m128 pos = g_pos + ( input->x*vertexShaderConstant[0] + input->y*vertexShaderConstant[1] + vertexShaderConstant[2] ) * g_size; _mm_storeu_ps(output->pos, pos); __m128i colors = _mm_loadl_epi64((__m128i*)&input->color); __m128iunpack = _mm_unpacklo_epi8(colors, g_zero); __m128ishuffle = _mm_shufflelo_epi16(unpack, _MM_SHUFFLE(3,0,1,2)); 		shuffle = _mm_shufflehi_epi16(shuffle, _MM_SHUFFLE(3,0,1,2)); __m128ipacked = _mm_packus_epi16(shuffle, g_zero); _mm_storel_epi64((__m128i*)&output->color, packed);  __m128tc = input->x*vertexShaderConstant[3] + input->y*vertexShaderConstant[4] + vertexShaderConstant[5]; _mm_storeu_ps(output->tc0, tc); aabb_min = _mm_min_ps(aabb_min, pos); aabb_max = _mm_max_ps(aabb_max, pos); 		++output; 		++input; } }
GFxQueue Draw Call 횟수

More Related Content

What's hot

ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門Fixstars Corporation
 
整数列圧縮
整数列圧縮整数列圧縮
整数列圧縮JAVA DM
 
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.kiki utagawa
 
qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...
qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...
qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...Adrian Huang
 
1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門NVIDIA Japan
 
Vivado hls勉強会1(基礎編)
Vivado hls勉強会1(基礎編)Vivado hls勉強会1(基礎編)
Vivado hls勉強会1(基礎編)marsee101
 
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層智啓 出川
 
レイトレ空間構造入門
レイトレ空間構造入門レイトレ空間構造入門
レイトレ空間構造入門Toru Matsuoka
 
文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding
文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding
文献紹介:TSM: Temporal Shift Module for Efficient Video UnderstandingToru Tamaki
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門Norishige Fukushima
 
【輪読】Taking the Human Out of the Loop, section 8
【輪読】Taking the Human Out of the Loop, section 8【輪読】Taking the Human Out of the Loop, section 8
【輪読】Taking the Human Out of the Loop, section 8Takeru Abe
 
Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14
Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14
Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14AMD Developer Central
 
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例Fixstars Corporation
 
OpenGL 3.2 and More
OpenGL 3.2 and MoreOpenGL 3.2 and More
OpenGL 3.2 and MoreMark Kilgard
 
[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images
[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images
[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB ImagesDeep Learning JP
 
Page cache in Linux kernel
Page cache in Linux kernelPage cache in Linux kernel
Page cache in Linux kernelAdrian Huang
 

What's hot (20)

CUDAメモ
CUDAメモCUDAメモ
CUDAメモ
 
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
 
整数列圧縮
整数列圧縮整数列圧縮
整数列圧縮
 
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
 
qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...
qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...
qemu + gdb: The efficient way to understand/debug Linux kernel code/data stru...
 
1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門1076: CUDAデバッグ・プロファイリング入門
1076: CUDAデバッグ・プロファイリング入門
 
Vivado hls勉強会1(基礎編)
Vivado hls勉強会1(基礎編)Vivado hls勉強会1(基礎編)
Vivado hls勉強会1(基礎編)
 
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
2015年度GPGPU実践プログラミング 第5回 GPUのメモリ階層
 
レイトレ空間構造入門
レイトレ空間構造入門レイトレ空間構造入門
レイトレ空間構造入門
 
Chokudai search
Chokudai searchChokudai search
Chokudai search
 
文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding
文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding
文献紹介:TSM: Temporal Shift Module for Efficient Video Understanding
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
 
実践QBVH
実践QBVH実践QBVH
実践QBVH
 
【輪読】Taking the Human Out of the Loop, section 8
【輪読】Taking the Human Out of the Loop, section 8【輪読】Taking the Human Out of the Loop, section 8
【輪読】Taking the Human Out of the Loop, section 8
 
Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14
Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14
Vertex Shader Tricks by Bill Bilodeau - AMD at GDC14
 
いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
 
OpenGL 3.2 and More
OpenGL 3.2 and MoreOpenGL 3.2 and More
OpenGL 3.2 and More
 
[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images
[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images
[DL輪読会]Pixel2Mesh: Generating 3D Mesh Models from Single RGB Images
 
コルーチンの使い方
コルーチンの使い方コルーチンの使い方
コルーチンの使い方
 
Page cache in Linux kernel
Page cache in Linux kernelPage cache in Linux kernel
Page cache in Linux kernel
 

Viewers also liked

헤테로지니어스 컴퓨팅 : CPU 에서 GPU 로 옮겨가기
헤테로지니어스 컴퓨팅 :  CPU 에서 GPU 로 옮겨가기헤테로지니어스 컴퓨팅 :  CPU 에서 GPU 로 옮겨가기
헤테로지니어스 컴퓨팅 : CPU 에서 GPU 로 옮겨가기zupet
 
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정funmeate
 
병렬 프로그래밍 패러다임
병렬 프로그래밍 패러다임병렬 프로그래밍 패러다임
병렬 프로그래밍 패러다임codenavy
 
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기Kiheon Park
 
Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Gruter
 
Dll파일 호출
Dll파일 호출Dll파일 호출
Dll파일 호출abapier
 
Cocos2d x a to z (상)
Cocos2d x a to z (상)Cocos2d x a to z (상)
Cocos2d x a to z (상)SeungIl Choi
 
Ccx03.cocos builder
Ccx03.cocos builderCcx03.cocos builder
Ccx03.cocos builder우진 이
 
GameMath-Chapter 01 벡터
GameMath-Chapter 01 벡터GameMath-Chapter 01 벡터
GameMath-Chapter 01 벡터Mark Choi
 
[NDC14] 파워포인트로 그래픽 리소스 만들기
[NDC14] 파워포인트로 그래픽 리소스 만들기[NDC14] 파워포인트로 그래픽 리소스 만들기
[NDC14] 파워포인트로 그래픽 리소스 만들기Sun Park
 
C# 뉴비를 위한 맛보기 2
C# 뉴비를 위한 맛보기 2C# 뉴비를 위한 맛보기 2
C# 뉴비를 위한 맛보기 2진상 문
 
Dom 생성과정
Dom 생성과정Dom 생성과정
Dom 생성과정abapier
 
4.5부동소수점
4.5부동소수점4.5부동소수점
4.5부동소수점JaeHong Park
 
3D Engine (ICON 2007)
3D Engine (ICON 2007)3D Engine (ICON 2007)
3D Engine (ICON 2007)SeongWan Kim
 
게임 개발자로서의 가치 창출
게임 개발자로서의 가치 창출게임 개발자로서의 가치 창출
게임 개발자로서의 가치 창출hongsungbock
 
Silverlight vs flash
Silverlight vs flashSilverlight vs flash
Silverlight vs flash권 태혁
 
GameMath-Chapter 13 발사체
GameMath-Chapter 13 발사체GameMath-Chapter 13 발사체
GameMath-Chapter 13 발사체Mark Choi
 
Chapter 2, 선형 변환과 행렬 1/2
Chapter 2, 선형 변환과 행렬 1/2Chapter 2, 선형 변환과 행렬 1/2
Chapter 2, 선형 변환과 행렬 1/2Thisisone Lee
 
Digital lighting and rendering
Digital lighting and renderingDigital lighting and rendering
Digital lighting and renderingJaeHong Park
 

Viewers also liked (20)

헤테로지니어스 컴퓨팅 : CPU 에서 GPU 로 옮겨가기
헤테로지니어스 컴퓨팅 :  CPU 에서 GPU 로 옮겨가기헤테로지니어스 컴퓨팅 :  CPU 에서 GPU 로 옮겨가기
헤테로지니어스 컴퓨팅 : CPU 에서 GPU 로 옮겨가기
 
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
모바일환경에서의 크로스 플랫폼_3D_렌더링엔진_제작과정
 
병렬 프로그래밍 패러다임
병렬 프로그래밍 패러다임병렬 프로그래밍 패러다임
병렬 프로그래밍 패러다임
 
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
박기헌 NDC12 초보 클라이언트 프로그래머의 병렬 프로그래밍 도전기
 
Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014
 
Dll파일 호출
Dll파일 호출Dll파일 호출
Dll파일 호출
 
Cocos2d x a to z (상)
Cocos2d x a to z (상)Cocos2d x a to z (상)
Cocos2d x a to z (상)
 
Ccx03.cocos builder
Ccx03.cocos builderCcx03.cocos builder
Ccx03.cocos builder
 
GameMath-Chapter 01 벡터
GameMath-Chapter 01 벡터GameMath-Chapter 01 벡터
GameMath-Chapter 01 벡터
 
[NDC14] 파워포인트로 그래픽 리소스 만들기
[NDC14] 파워포인트로 그래픽 리소스 만들기[NDC14] 파워포인트로 그래픽 리소스 만들기
[NDC14] 파워포인트로 그래픽 리소스 만들기
 
C# 뉴비를 위한 맛보기 2
C# 뉴비를 위한 맛보기 2C# 뉴비를 위한 맛보기 2
C# 뉴비를 위한 맛보기 2
 
투영 공식
투영 공식투영 공식
투영 공식
 
Dom 생성과정
Dom 생성과정Dom 생성과정
Dom 생성과정
 
4.5부동소수점
4.5부동소수점4.5부동소수점
4.5부동소수점
 
3D Engine (ICON 2007)
3D Engine (ICON 2007)3D Engine (ICON 2007)
3D Engine (ICON 2007)
 
게임 개발자로서의 가치 창출
게임 개발자로서의 가치 창출게임 개발자로서의 가치 창출
게임 개발자로서의 가치 창출
 
Silverlight vs flash
Silverlight vs flashSilverlight vs flash
Silverlight vs flash
 
GameMath-Chapter 13 발사체
GameMath-Chapter 13 발사체GameMath-Chapter 13 발사체
GameMath-Chapter 13 발사체
 
Chapter 2, 선형 변환과 행렬 1/2
Chapter 2, 선형 변환과 행렬 1/2Chapter 2, 선형 변환과 행렬 1/2
Chapter 2, 선형 변환과 행렬 1/2
 
Digital lighting and rendering
Digital lighting and renderingDigital lighting and rendering
Digital lighting and rendering
 

Similar to 이권일 Sse 를 이용한 최적화와 실제 사용 예

11_빠른 개발 가능한 레벨 편집 시스템
11_빠른 개발 가능한 레벨 편집 시스템11_빠른 개발 가능한 레벨 편집 시스템
11_빠른 개발 가능한 레벨 편집 시스템noerror
 
[조진현]Kgc2012 c++amp
[조진현]Kgc2012 c++amp[조진현]Kgc2012 c++amp
[조진현]Kgc2012 c++amp진현 조
 
ffmpeg optimization using CUDA
ffmpeg optimization using CUDAffmpeg optimization using CUDA
ffmpeg optimization using CUDAyyooooon
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 명신 김
 
Deview 2019 눈발자국
Deview 2019 눈발자국Deview 2019 눈발자국
Deview 2019 눈발자국hanbeom Park
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2Chris Ohk
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013Esun Kim
 
[조진현] [Kgc2011]direct x11 이야기
[조진현] [Kgc2011]direct x11 이야기[조진현] [Kgc2011]direct x11 이야기
[조진현] [Kgc2011]direct x11 이야기진현 조
 
2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)
2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)
2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)khuhacker
 
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요NAVER D2
 
Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900Samsung Electronics
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010Ryan Park
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10Ryan Park
 
[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기
[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기
[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기OpenStack Korea Community
 
NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스Sungik Kim
 
Reproducible research(2)
Reproducible research(2)Reproducible research(2)
Reproducible research(2)건웅 문
 
하이퍼레저 패브릭 데이터 구조
하이퍼레저 패브릭 데이터 구조하이퍼레저 패브릭 데이터 구조
하이퍼레저 패브릭 데이터 구조Logpresso
 
R 스터디 세번째
R 스터디 세번째R 스터디 세번째
R 스터디 세번째Jaeseok Park
 
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이GangSeok Lee
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성HyeonSeok Choi
 

Similar to 이권일 Sse 를 이용한 최적화와 실제 사용 예 (20)

11_빠른 개발 가능한 레벨 편집 시스템
11_빠른 개발 가능한 레벨 편집 시스템11_빠른 개발 가능한 레벨 편집 시스템
11_빠른 개발 가능한 레벨 편집 시스템
 
[조진현]Kgc2012 c++amp
[조진현]Kgc2012 c++amp[조진현]Kgc2012 c++amp
[조진현]Kgc2012 c++amp
 
ffmpeg optimization using CUDA
ffmpeg optimization using CUDAffmpeg optimization using CUDA
ffmpeg optimization using CUDA
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
 
Deview 2019 눈발자국
Deview 2019 눈발자국Deview 2019 눈발자국
Deview 2019 눈발자국
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
 
[조진현] [Kgc2011]direct x11 이야기
[조진현] [Kgc2011]direct x11 이야기[조진현] [Kgc2011]direct x11 이야기
[조진현] [Kgc2011]direct x11 이야기
 
2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)
2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)
2015 제2회 동아리 해커 세미나 - 병렬컴퓨팅 소개 (16기 김정현)
 
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
 
Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
 
[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기
[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기
[OpenInfra Days Korea 2018] Day 2 - E1: 딥다이브 - OpenStack 생존기
 
NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스
 
Reproducible research(2)
Reproducible research(2)Reproducible research(2)
Reproducible research(2)
 
하이퍼레저 패브릭 데이터 구조
하이퍼레저 패브릭 데이터 구조하이퍼레저 패브릭 데이터 구조
하이퍼레저 패브릭 데이터 구조
 
R 스터디 세번째
R 스터디 세번째R 스터디 세번째
R 스터디 세번째
 
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성
 

이권일 Sse 를 이용한 최적화와 실제 사용 예

  • 1. SSE 를 이용한 최적화와 실제 사용 예 이권일 EA Seoul Studio (BFO)
  • 2. 발표 대상 C/C++ 프로그래머 H/W 및 최적화에 관심 있는 자 GPGPU 를 준비하는 자
  • 3. SSE (SIMD Streaming Extension) 1999년 펜티엄3 에 처음 포함된 확장 기능 Float Point 및 비교 로직 등 다양한 연산 SSE 전용 128bit XMM 레지스터 8개 추가 MMX 와 달리 거의 모든 기능이 구현됨
  • 5. SIMD 연산 일반연산 1.0 2.0 3.0 4.0 1.0 5.0 6.0 7.0 8.0 5.0 6.0 8.0 10.0 12.0 6.0
  • 6.
  • 7. SSE 2 부터 새로이 추가된 __int64 와 double 을 지원하기 위한 __m128i, __m128d 자료형도 있음
  • 8.
  • 9. 편하게 코딩하기 // 산술 연산자 __forceinline__m128operator+(__m128 l, __m128 r) { return_mm_add_ps(l,r); } __forceinline__m128operator-(__m128 l, __m128 r) { return_mm_sub_ps(l,r); } __forceinline__m128operator*(__m128 l, __m128 r) { return_mm_mul_ps(l,r); } __forceinline__m128operator/(__m128 l, __m128 r) { return_mm_div_ps(l,r); } __forceinline__m128operator+(__m128 l, float r) { return_mm_add_ps(l,_mm_set1_ps(r)); } __forceinline__m128operator-(__m128 l, float r) { return_mm_sub_ps(l, _mm_set1_ps(r)); } __forceinline__m128operator*(__m128 l, float r) { return_mm_mul_ps(l, _mm_set1_ps(r)); } __forceinline__m128operator/(__m128 l, float r) { return_mm_div_ps(l, _mm_set1_ps(r)); } // 논리 연산자 __forceinline__m128operator&(__m128 l, __m128 r) { return_mm_and_ps(l,r); } __forceinline__m128operator|(__m128 l, __m128 r) { return_mm_or_ps(l,r); } // 비교 연산자 __forceinline__m128operator<(__m128 l, __m128 r) { return_mm_cmplt_ps(l,r); } __forceinline__m128operator>(__m128 l, __m128 r) { return_mm_cmpgt_ps(l,r); } __forceinline__m128operator<=(__m128 l, __m128 r) { return_mm_cmple_ps(l,r); } __forceinline__m128operator>=(__m128 l, __m128 r) { return_mm_cmpge_ps(l,r); } __forceinline__m128operator!=(__m128 l, __m128 r) { return_mm_cmpneq_ps(l,r); } __forceinline__m128operator==(__m128 l, __m128 r) { return_mm_cmpeq_ps(l,r); }
  • 10. SIMD 정말 4배 빠른가요? // C 버젼 for(size_ti=0; i<count;++i) { b[i] = a[i] + a[i]; } -> 실행 시간 49.267 ms // Compiler Intrinsic 버젼 for(size_ti=0; i<count/4;++i) { b4[i] = a4[i] + a4[i]; } -> 실행 시간 47.927 ms
  • 11. 메모리 병목!! a[0] b[0] + a[1] b[1] + a[2] B[2] + a[3] b[3] + a[4] + a[5] + a[0] b[0] a[1] b[1] a[2] b[2] a[3] b[3] a[4] a[5] a[6] a[7] + + + + + + +
  • 12. 연산량을 늘리자! sinf() // sin(a) = a – (a^3)/3! + (a^5)/5! – (a^7)/7! … float req_3f = 1.0f / (3.0*2.0*1.0); float req_5f = 1.0f / (5.0*4.0*3.0*2.0*1.0); float req_7f = 1.0f / (7.0*6.0*5.0*4.0*3.0*2.0*1.0); for(size_ti=0; i<count; ++i) { b[i] = a[i] - a[i]*a[i]*a[i]*req_3f + a[i]*a[i]*a[i]*a[i]*a[i]*req_5f - a[i]*a[i]*a[i]*a[i]*a[i]*a[i]*a[i]*req_7f; } -> 실행 시간 111. ms
  • 13. C 언어의 연산 병목 a[0] b[0] + a[1] b[1] + a[2] b[2] + a[3] b[3] + a[4] + a[0] a[1] a[2] a[3] a[4] b[0] b[1] b[2] b[3] + + + + +
  • 14. SSE 버젼의 sinf() // sin(a) = a – (a^3)/3! + (a^5)/5! – (a^7)/7! … __m128 req_3f4 = _mm_set1_ps(req_3f); __m128 req_5f4 = _mm_set1_ps(req_5f); __m128 req_7f4 = _mm_set1_ps(req_7f); for(size_ti=0; i<count/4; ++i) { b4[i] = a4[i] - a4[i]*a4[i]*a4[i]*req_3f4 + a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*req_5f4 - a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*a4[i]*req_7f4; } -> 실행 시간 48.939 ms
  • 15. SSE는 아직도 메모리 병목!! a[0,1,2,3] + b[0,1,2,3] a[4,5,6,7] + b[4,5,6,7] a[8,9,10,11] + b[8,9,10,11] a[12,13,14,15] + b[12,13,14,15] a[16,17,18,19] a[0,1,2,3] b[0,1,2,3] a[4,5,6,7] b[4,5,6,7] a[8,9,10,11] b[8,9,10,11] a[12,13,14,15] b[12,13,14,15] a[16,17,18,19] + + + +
  • 16.
  • 17.
  • 18.
  • 20. _mm_stream_ps() // C 버젼 for(size_ti=0; i<count;++i) { b[i] = a[i] + a[i]; } -> 실행 시간 49.267 ms // a+a stream 버젼 for(size_ti=0; i<count/4;++i) { _mm_stream_ps((float*)(b4+i), _mm_add_ps(a4[i], a4[i])); } -> 실행 시간 30.114 ms
  • 21. CPU _mm_stream_ps() 의 작동 Excution Unit L1 Cache L2 Cache WC Buffer Memory BUS Memory
  • 22.
  • 23.
  • 24. Stream 을 추가한 그래프 !!
  • 25. 같은 시간에 더 많은 일을 합시다!! float Read + Write 시간 : 2.896 ns __m128 Read + Write 시간 : 11.214 ns __m128 Read + Stream 시간 : 6.977 ns
  • 26. SSE 프로그래밍 메모리 접근 시간이 길어지고 연산시간이 짧아짐에 따라 더 많은 계산을 할 수 있다. 요즘 CPU는 Out-of-Order 로 인해 대부분 비동기 실행을 한다. 적극 이용하자. 병렬화와 병목 문제는 GPGPU 연산에도 동일하게 적용된다. 미래를 대비하자.!!
  • 27. SSE 를 적용한 예제들
  • 28. SSE 를 사용한 CPU Skinning Vertex : 1024 * 1024 Bone : 200 4 weight per vertex + normal + tangent SSE 컴파일 옵션이 켜진 C, SSE최적화 스키닝 없는 C 루프 복사, SSE 루프 복사, memcpy()
  • 29. C Skinning Code // Optimized C Version D3DXMATRIX m = b[in->index[0]] * in->blend[0] + b[in->index[1]] * in->blend[1] + b[in->index[2]] * in->blend[2] + b[in->index[3]] * in->blend[3]; out->position.x = in->position.x*m._11 + in->position.y*m._21 + in->position.z*m._31 + m._41; out->position.y = in->position.x*m._12 + in->position.y*m._22 + in->position.z*m._32 + m._42; out->position.z = in->position.x*m._13 + in->position.y*m._23 + in->position.z*m._33 + m._43; out->normal.x = in->normal.x*m._11 + in->normal.y*m._21 + in->normal.z*m._31; out->normal.y = in->normal.x*m._12 + in->normal.y*m._22 + in->normal.z*m._32; out->normal.z = in->normal.x*m._13 + in->normal.y*m._23 + in->normal.z*m._33; out->tangent.x = in->tangent.x*m._11 + in->tangent.y*m._21 + in->tangent.z*m._31; out->tangent.y = in->tangent.x*m._12 + in->tangent.y*m._22 + in->tangent.z*m._32; out->tangent.z = in->tangent.x*m._13 + in->tangent.y*m._23 + in->tangent.z*m._33;
  • 30. SSE Skinning Code // SSE Code __m128 b0 = _mm_set_ps1(in->blend[0]); __m128 b1 = _mm_set_ps1(in->blend[1]); __m128 b2 = _mm_set_ps1(in->blend[2]); __m128 b3 = _mm_set_ps1(in->blend[3]); __m128* m[4] = { (__m128*)( matrix+in->index[0] ), (__m128*)( matrix+in->index[1] ), (__m128*)( matrix+in->index[2] ), (__m128*)( matrix+in->index[3] ) }; __m128 m0 = m[0][0]*b0 + m[1][0]*b1 + m[2][0]*b2 + m[3][0]*b3; __m128 m1 = m[0][1]*b0 + m[1][1]*b1 + m[2][1]*b2 + m[3][1]*b3; __m128 m2 = m[0][2]*b0 + m[1][2]*b1 + m[2][2]*b2 + m[3][2]*b3; __m128 m3 = m[0][3]*b0 + m[1][3]*b1 + m[2][3]*b2 + m[3][3]*b3; _mm_stream_ps( out->position, m0*in->position.x+m1*in->position.y+m2*in->position.z+m3 ); _mm_stream_ps( out->normal, m0*in->normal.x+m1*in->normal.y+m2*in->normal.z ); _mm_stream_ps( out->tangent, m0*in->tangent.x+m1*in->tangent.y+m2*in->tangent.z );
  • 31. SSE Skinning 결과 memcpy() 시간의 80% 로 스키닝을 할 수 있다. 파티클, UI 등에 유용하게 사용할 수있다. Dynamic VB 를 쓰는 동안 계산을 추가로 할 수 있다.
  • 32.
  • 33. Deep-Narrow Tree 를 만들어야 효율이 좋아지므로 노드가 무척 많아진다.
  • 34.
  • 37. Scaleform과 SSE Flash 파일을 3D 가속을 받으며 실행 가능하도록 만들어진 라이브러리 Direct3D/OpenGL 및 다양한 렌더링 라이브러리 지원 현재 프로젝트의 UI 제작에 사용 209개 파일 65147 Line 의 Acton Script 와 DXT5 79MB UI 이미지
  • 38. Scaleform 3.1 의 문제점 복잡한 swf들을 다수 사용할 경우 CPU 사용률이 상당히 높다. 높은 자유도가 GPU에 최적화 되기 어려운 UI 를 만들게 한다. GRendererD3D9 은예제 코드에 가깝고 개발시 H/W 특성이 고려되지 않았다.
  • 39. Scaleform개선 방향 Client GFx Client GFx GFxQueue Direct3D Direct3D GFxMoveView::Advance() GFxMoveView::Advance() SceneMgr::DrawScene() GFxMoveView::DisplayMT() SceneMgr::DrawScene() GFxQueue::DrawPrim() GFxMoveView::Display() GFxQueue::Flush() ID3DDevice::DrawPrim() 5~15ms/frame ID3DDevice::DrawPrim()
  • 40. GFxQueue의 Batch 합치기 기능 Batch 합치기를 하기 위해 Vertex 를 Queue 에 넣을때 Transform (TnL) 을 미리 처리 Render State, Texture State 를 체크해서 중복된 렌더링 재설정을 방지 Scene 에서 벗어난 Shape 들안그리는 기능 추가 CPU로 대체된 VertexShader는 삭제, Pixel Shader도 Batch 합치기를 위해 수정
  • 41. Transform 코드 caseVS_XY16iCF32: { XY16iCF32_VERTEX* input = (XY16iCF32_VERTEX*)src + start; for(UINT i=0; i<count; ++i){ //output->pos.x = g_x + (input->x * vertexShaderConstant[0].x + input->y * vertexShaderConstant[1].x + vertexShaderConstant[2].x) * g_width; //output->pos.y = g_y - (input->x * vertexShaderConstant[0].y + input->y * vertexShaderConstant[1].y + vertexShaderConstant[2].y) * g_height; //output->pos.z = 1; //output->pos.w = 1; //output->color = FlipColor(input->color); //output->factor = FlipColor(input->factor); //output->tc0.x = input->x * vertexShaderConstant[3].x + input->y * vertexShaderConstant[4].x + vertexShaderConstant[5].x; //output->tc0.y = input->x * vertexShaderConstant[3].y + input->y * vertexShaderConstant[4].y + vertexShaderConstant[5].y; //aabb.AddPoint(output->pos); __m128 pos = g_pos + ( input->x*vertexShaderConstant[0] + input->y*vertexShaderConstant[1] + vertexShaderConstant[2] ) * g_size; _mm_storeu_ps(output->pos, pos); __m128i colors = _mm_loadl_epi64((__m128i*)&input->color); __m128iunpack = _mm_unpacklo_epi8(colors, g_zero); __m128ishuffle = _mm_shufflelo_epi16(unpack, _MM_SHUFFLE(3,0,1,2)); shuffle = _mm_shufflehi_epi16(shuffle, _MM_SHUFFLE(3,0,1,2)); __m128ipacked = _mm_packus_epi16(shuffle, g_zero); _mm_storel_epi64((__m128i*)&output->color, packed); __m128tc = input->x*vertexShaderConstant[3] + input->y*vertexShaderConstant[4] + vertexShaderConstant[5]; _mm_storeu_ps(output->tc0, tc); aabb_min = _mm_min_ps(aabb_min, pos); aabb_max = _mm_max_ps(aabb_max, pos); ++output; ++input; } }
  • 43. GFx Renderer 코멘트 GRenderD3D9 코드가 구리다. 프로그래머라면 찬찬히 분석한다음 여러군데 손을 봐두자. UI 아티스트는 GPU 최적화에신경쓰지 않는다. 초기 단게부터 적절한 레이아웃과 컴포넌트를 설계해두자. GFxExport에서 DXTn포맷을 무조건 2의 배수로 Resize 해버려 저장하는 경우가 있다. GFxExport에서 Texture Atlas 기능을 쓰는 것도 최적화에 큰 도움이 된다.
  • 44. ?

Editor's Notes

  1. 이 그림은 일반적인 프로그래밍을 할때 32비트, 64비트 프로그래밍을 할때 사용하는 레지스터들입니다.왼쪽에 있는 RAX 부터 R15까지가 범용 레지스터로 RSP 까지의 흰색 부분이 32비트 범용 레지스터이고 그것을 R15 까지 확장한 것이 64비트 범용 레지스터입니다.가운데 있는 것이 64비트 크기의 FPU/MMX 겸용으로 사용되는 레지스터입니다. FPU는 이것을 80비트까지 확장해서 쓰기도 하는데 64비트 프로그래밍 시에는 이 부분을 사용하지 않도록 권장하고 있습니다.그리고 마지막 SSE 용 128 비트 레지스터인 XMM 레지스터 입니다. 0번부터 7번까지는 32비트에서 사용하고 64비트에서는 15번까지 전부 사용할 수 있습니다.크기로 비교해보면 32비트 모드일때나 64비트 모드일때나 XMM 레지스터 크기가 훨씬 크다는 것을 알 수 있습니다.그만큼 더 많은 일을 시킬 수 있다는 뜻이죠.
  2. XMM 레지스터가 이렇게 거대해진 이유는 SIMD 연산을 하기 위해서 입니다.SIMD 는 Single Instruction Multi Data 의 약자라고 했는데 이 그림을 보면 쉽게 이해하실 수 있을겁니다.왼쪽에 있는 것이 일반적인 연산이라면 오른쪽 같이 4개를 한꺼번에 처리하는 것이 SIMD 처리입니다.SIMD는 하나의 명령으로 여러개의 데이터를 동시에 처리하기 때문에 다량의 데이터를 처리해야할때 매우 효율적입니다.
  3. 이렇게 빠른 작동을 할 수 있는 것은 Write Combining 버퍼라는 것 때문입니다.처음 소개된 것은 PCI 버스 시절인데 버스에 접근 할때는 캐쉬를 끄고 접근하기 때문에 접근 속도가 매우 느려지는데그것을 보완하기 위해서 메모리 컨트롤러단에 몇십 바이트 크기의 쓰기 버퍼를 만들어 놓고 캐쉬를 통하지 않는 쓰기 작업을 도와주도록 한 것입니다.이것은 그래픽 카드에 많이 쓰였고 옛날 Direct Draw 프로그래밍 시절 Surface 를 읽어오는 것에 비해 쓰는 것이 월등히 빨랐던 것도 WC 버퍼 덕분입니다._mm_stream_ps() 명령은 이 WC 버퍼를 통해 데이터 쓰기 작업을 도와줍니다.
  4. SSE 프로그래밍을 사용한 예제를 세가지 준비했습니다.모든 예제들은 실제 상용화된 프로젝트나 내부 툴 코드에 사용된 경험이 있는 것들이고 더 자세한 정보가 필요하신 분들은 개별적으로 접촉하시면 상세한 정보를 나눠드릴 수 있습니다.
  5. 마지막으로 Scaleform최적화 부분입니다.사실 Scaleform최적화는 별도 세션으로 다루고 싶었는데 준비 시간 문제도 있었고저는 스케일폼보다 SSE 최적화쪽이 훨씬 익숙하기 때문에 아직 준비가 덜 되었다고생각되어서 이렇게 이 밑에 붙였습니다.스케일폼은 게임내에서 매크로 미디어 플래쉬 파일을 구동할 수 있게 해주는 라이브러리입니다. 요즘 게임 UI 개발에 많이 사용하고 있고 저희 팀도 꽤 일찍 도입해서 아주 많은부분을 GFx에 의지하고 있습니다.참고로 저희 프로젝트에서는 SWF 파일이 209개이고 액션 스크립트만 65000라인, 그리고배경 이미지를 제외한 UI 컴포넌트에 쓰인 이미지만 압축텍스쳐로 79MB에 달합니다.
  6. Scaleform을 최적화 하게된 이유는 저희 프로젝트에서 아주 많은 부분을이 라이브러리에 의존하고 있으며 덕분에 상당히 많은 부하가 GFx에걸리고 있기 때문입니다. 최적화 이전에 부하가 심할 경우 15ms 정도가 꾸준히GFx에 의해서 사용되었고 GFx하나만으로 프레임 레이트가 팍팍 떨어지는문제를 발생시키고 있었습니다.어쨌거나 GFx라이브러리는 방대하고 코드 핵심 부분은 고치기 어려운 만큼소극적인 방법으로 최적화를 진행했고 여러가지 방법을 동원해서 게임에 부하를획기적으로 줄일 수 있도록 부하를 분산할 수 잇었습니다.이자리를 빌어 GFx최적화 아이디어를 주신 전 네오위즈 최의종 팀장님께 감사의말씀을 드립니다.
  7. GFx의 최대 문제점은 Display 를 호출한 시점에 여러가지 CPU 연산을 수행하고 그 다음 3D 렌더링이 진행된다는 점입니다. CPU 부하가 적은 프로젝트에서는 큰부하가 되지 않지만 CPU 부하가 심할 경우 이러한 부하는 매우 부담스럽습니다.저희 프로젝트의 경우 최종적으로 전체 swf파일을 그리는 Display() 를 호출했을때5~15ms 까지의 시간이 걸렸었습니다. 여기서 아주 많은 UI 컴포넌트들을 그리는데개중의 많은 시간은 SWF 파일들을 돌아다니며 애니메이션을 처리하고 좌표를 정하는등의 연산 작업이었습니다.게다가 문제는 이 부하는 게임에서 전투가 격해지고 캐릭터들이 화면에 많이 나올때 더더욱 심해졌다는 점입니다. 15ms 정도라면 상당히 긴 시간이라서 GFx렌더링 만 하더라도 60fps 를 겨우 그릴 수 있는 상태였습니다.저희 팀에서는 이 문제를 GRenderer를 멀티쓰레드화 시키는 것으로 해결하였습니다. GFx라이브리 자체는 매우 크고 복잡하기 때문에 쉽게멀티쓰레드화 시키지 힘들지만 다행히도 Display() 함수를 호출한 다음은GFx라이브러리 외부와의 호출이나 데이터 접근이 존재하지 않고 단지화면 출력을 위한 Direct3D 호출만이 이뤄지고 있었습니다.따라서 Display() 함수 자체를 다른 쓰레드로 옮기고 화면에 렌더링하고자하는 데이터들을 전부 큐에 넣은 다음 게임 화면 렌더링이 끝나면 큐에들어있는 데이터를 한번에 그리도록 하였습니다. 이것은 DirectX 11 에 추가된 큐와 매우 흡사한 버젼입니다.
  8. 이 큐는 단순히 Direct3D함수 호출만을 큐하는 것이 아니라 미리 전처리 할 수 있는 것들을처리해두는 기능을 추가하였습니다. 가장 간단한 것으로 RenderState가 중복되어 설정되는것을 막아주고 동일한 텍스쳐와 쉐이더를 사용하는 오브젝트들을 한꺼번에 그릴 수 있도록배치 처리를 해주었습니다. 또한 UI 제작 아티스트 분들이 UI를 감추기 위해 컴포넌트들을 화면 밖으로 살짝 옮겨놓는 경우가 있는데 이런 것들에 대한 호출도 실행하지 않도록 하였죠.GFx는 버텍스 쉐이더에서 행렬을 써서 UI 의 좌표를 지정했는데 배치 합치기와 클립핑 작업을하기 위해서 버텍스 쉐이더 코드를 전부 CPU 단으로 옮겼습니다. 이 코드들은 SSE 로 재작성되었고 몇몇 픽셀 쉐이더 상수들도 버텍스 데이터를 통해 전달할 수 있도록 바뀌었습니다.
  9. 이 코드가 버텍스 쉐이더를 대체한 CPU 코드의 일부입니다. SSE 코딩을 하기 앞서먼저 C 코드로 잘 작동하는 코드를 작성한 다음 SSE 로 옮겼습니다.GFx최적화를 하며 애먹었던게 구형 그래픽 카드인데 프로젝트 스펙상 쉐이더 모델2.0을 지원해야 했고 라데온 9600 과 그 것을 승계한 x1600 같은 그래픽 카드는 Free Vertex Format 을 제대로 지원하지 않앗기 때문에 포맷에 맞춰쳐 Vertex 구조체를 사용해야하는 문제가 발생했습니다. (덕분에 패치 한번 깨먹었습니다.  )속도를 위해 16바이트 정렬된 Vertex 를 사용하였었는데 쉐이더 모델 2.0을 위해하드웨어에 맞게 Vertex 정보를 사용하였고 덕분에 _mm_storeu_ps() 같은 명령을쓰게 되었죠.
  10. 이 그래프는 큐를 이용함으로써 배치 합치기의 결과를 보여주는 그래프 입니다.왼쪽부터 로그인 화면, 로비 화면, 상점 화면, 대기실, 로딩, 게임 화면, 게임내 특수 UI 화면등을 순서대로 띄웠을 때이고 그래프는 각각 화면에서 Draw Call횟수를 나타내고 있습니다.아주 만족스러운건 아니지만 경우에 따라서 1/3 ~ 1/4 정도 Draw Call 횟수가줄어들었으며 큐를 사용함에 따라 CPU 부하가 전부 다른 쓰레드로 감춰진 관계로 2 Core 이상 있는 머신에서는 프레임 레이트가 대폭 향상 되었습니다.참고로 Call횟수가 기대보다 줄어들지 못한 이유는 UI 설계상 이유도 있는데가량 캐릭터 머리위에 떠있는 이름의 경우 아이콘-이름-아이콘-이름이 차례대로 있는 컴포넌트를 그릴 경우 아이콘들이 같은 텍스쳐에 있더라도 큐는상태가 변경됨에 따라 4번의 Draw Call 별도로 호출해줘야 한다는 점 입니다.특히 폰트 텍스쳐가 다른 아이콘이나 UI 들과 별도로 있는 관계로 그리는 순서에 매우 민감하게 반응합니다. 또한 컴포넌트들을 어떻게 조합하냐에 따라한번에 주루룩 그려주는 경우도 있을 수 있고 전부 별도로 그리는 경우도 있기 때문에 디자인 단계에서 최적화를 고려하지 않으면 코드만으로는개선에 한계가 있습니다.
  11. 스케일 폼에 대해 결론을 내자면..일단 스케일 폼에 따라온 GRenderD3D9 코드는 매우 구립니다. 매니지드 텍스쳐도제대로 지원하지 못하고 메모리 관리자도 너무 많은 메모리를 사용합니다. 시간이있다면 차분히 이곳저곳을 분석해서 손을 대줘야 합니다.그리고 UI 아티스트들이 작업하는 초기부터 여러가지 신경써서 작업할 수 있도록해야 합니다. 나중에 손을 대면 늦고 최적화가 힘든 만큼 텍스쳐를 어떻게 쓰고있는지 그리는 순서를 어떻게 하는게 좋은지 미리 미리 알려주고 작업 결과물을틈틈히 체크해 주어야 합니다.GFXExport라는 툴이 있는데 이 툴도 문제가 좀 많습니다. 저희 프로젝트는 압축텍스쳐를 사용하는데 여기 사용된 텍스쳐 압축 라이브러리가 최적화가 꺼져 있다던지 압축을 사용할 경우 무조건 이미지를 2의 배수로 resize 한다던지 문제가 꽤많습니다.그리고 배치 합치기 작업을 계획한다면 GFxExport옵션을 적절히 줘서 텍스쳐아틀라스를 활성화 해주는게 좋습니다. 이게 안되어 있으면 텍스쳐가 바뀌면서모아 그리는게 매우 힘들어지니까요.참고로 멀티쓰레드를 써서 GFx가 소모하는 시간은 전체 렌더링에서 1/4~1/3 정도로 대폭 줄었습니다. DirectX 호출 시간은 줄일 수 없지만 나머지 CPU 작업부분들은 전부 줄일 수 있었습니다.다시한번 아이디어를 주신 최의종 팀장님께 감사의 말을 전하고 저는 이만 발표를 마치겠습니다.