CUDA 를게임프로젝트에적용하기 유영천 - 모여서각자코딩하는모임 https://megayuchi.com tw: @dgtman
GPGPU(General-Purpose computing on GPU GPU 를사용하여 CPU 가전통적으로취급했던응용프로그램들의계산을수행하는기술 GPU 코어 1 개의효율은 CPU 코어 1 개에비해많이떨어지지만코어의개수가엄청나게많다. 많은수의코어를사용하면산술술연산성능 (Throughput) 은 CPU 의성능보다훨씬뛰어나다.
GPGPU 의특징 강점 엄청난수의스레드를사용할수있다. 부동소수점연산이엄청빠르다. 컨텍스트스위칭이엄청빠르다. 약점 흐름제어기능자체가빈약하다. 프로그래밍기법의제약 ( 재귀호출등 ) Core 당클럭이 CPU 에비해많이느리다 (1/3 1/2 수준 )
GPU vs CPU GM < ν Gundam - 택도없는승부. 이것은학살.
GPU vs CPU GM > ν Gundam 한판붙자!!!
적용분야 Physics Simulation Video Processing Image Processing Astrophysics Medical Imaging More
Image Processing 예제 2885x4102 크기의이미지에 5x5 Gaussian Blur 를 3 회적용
2885x4102 크기의이미지에 5x5 Gaussian Blur 를 3 회적용 성능비교 1Threads - Intel i7 8700K @4.5GHz -> 7512.5ms 12Threads - Intel Intel i7 8700K @4.5GHz -> 754.6ms CUDA GTX970 (13 SM x 128 Core = 1664 Core) -> 53.7ms 참고 ) 이미지필터링코드는 CUDA, CPU 완전동일. 알고리즘상의최적화는없음. 병렬처리효율의비교를위한테스트임.
사용가능한 S/W 제품들 nvidia CUDA Direct X Direct Compute Shader( 실질적으로게임전용 ) Open CL Microsoft AMP C++
CUDA
CUDA (Compute Unified Device Architecture) C 언어등산업표준언어를사용하여 GPU 에서작동하는병렬처리코드를작성할수있도록하는 GPGPU 기술 nvidia 가개발, 배포. 그래서 nvidia GPU 만가능. 비슷한기술로 OpenCL, Direct Compute Shader 가있음.
CUDA 의장점 자료가많다. 유일하게 GPU 상에서의디버깅이가능하다. C/C++ 에서의포팅이쉽다. 포인터사용가능!!!! 경쟁제품들이다사망...
CUDA 프로그래밍 용어및기본요소들
Thread CPU 의 Thread 와비슷 하다 ( 같지않다 ). GPU 의 Core( 혹은 SP) 에맵핑 독립적인컨텍스트를가지고있다 ( 라고해봐야연산에필요한컨텍스트뿐 ). 컨텍스트스위칭이빠르다. 독립적인흐름제어는불가능하다. Warp(32 개스레드 ) 단위로묶여서움직임 ).
Block Block -> N 개의 Thread 집합. SM 에맵핑 동일 Block 의스레드는 L1 cache 와 Shared Memory 를공유한다. 스레드만잔뜩있으면될것같은데왜이런게있냐면 그것은아마도 CPU 의 core! GPU 의 core CPU 의 core GPU 의 SM 이기때문일것이다.
Grid Block 들의집합 그냥쉽게생각해서그래픽카드 -> Grid
GPU 구조의이해 (GP104) *CPU 의 Core 에해당하는것은 GPU 의 SM 이다. 따라서흐름제어의측면으로보면 2560 Core CPU 가아니라 20 Core CPU 에상응한다.
Thread, Block, Grid Thread -> Core Block -> SM Grid -> GPU
Kernel GPU 에서돌아가는 C 함수 CPU 멀티스레드프로그래밍에서의 Thread 함수와같다. 복수의 Thread 에의해실행된다. CPU 측 (host) 에서호출되며 GPU 측 (device) 코드를호출할수있다.
N x N 매트릭스의합을구하는 CUDA 커널함수
CUDA Compute Shader 대응 H/W 연산단위 Thread Thread SP or CUDA Core HW 점유단위 Block Group SM Shared Memory 48KB, shared 16KB, groupshared L1 Cache (16KB + 4 8KB) Barrier 언어 syncthreads() C/C++,ptr 사용가능 GroupMemoryBarrie rwithgroupsync() HLSL, similar to C
CUDA 프로그래밍 Memory
CUDA Memory Global Memory 보통말하는 Video Memory. 크다. 느리다. Register CPU 의레지스터와같다. 로컬변수, 인덱싱하지않고크기가작은로컬배열은레지스터에맵핑된다. 작다. 빠르다. Shared Memory Block 의스레드들이공유하는메모리. 블럭당 48KB. 작다. 빠르다. Constant memory GPU 하드웨어에구현된읽기전용 64KB 메모리. 상황에따라 Global Memory 로도구현. 캐싱됨. 상황에따라 L1 Cache 와동일속도. Texture Memory 읽기전용. Global Memory 위에구현되지만 Texture 로서읽는경우공간지역성을가짐. 캐싱됨. 참고 ) SM 에하나씩존재하는 64KB 의 cache 를 16KB L1 캐쉬와 48KB Shared Memory 로사용
CUDA 메모리
Texture memory caching
메모리억세스범위 Thread -> Local Memory, Shared Memory, Global Memory Block -> Shared Memory, Global Memory Grid -> Global Memory(Launch Kernel)
CUDA 프로그래밍 GPU 코드스케쥴링
SIMT(Single Instruction Multiple Threads) 동일명령어를여러개의 Thread 가동시실행 GPU 는기본적으로 SIMT 로작동 N 차원벡터를다루는산술처리에선적합 Thread 중일부가분기를하거나루프를하면나머지 Thread 들은대기 -> 병렬처리의의미가없어짐.
WARP 의이해 Thread 들을묶어서스케쥴링하는단위 현재까지는모든 nvidia GPU 의 1 Warp 는 32 Thread. 즉 32 Thread 는늘같은같은코드어드레스를지나감. 동시수행가능한 Warp 수는 SM 의 Warp 스케쥴러개수에의존. SM 당 2-4 개의 Warp 를동시에실행가능
WARP 의이해 32 Thread 중 1 개의 Thread 만이분기해서 Loop 를돌고있으면 31 개의 Thread 는 1 개의 Thread 가 Loop 를마치고같은코드어드레스를수행할수있을때까지대기. 각 Thread 가다른루프, 다른흐름을타는것은엄청난성능저하를부른다.
SM CPU Core 2 인 3 각경기
CUDA 기준 GPU 세대별하드웨어특성 Compute Capability 2.0 2.1 3.0 3.2 3.5 3.7 5.0 5.2 5.3 6.0 6.1 6.2 SM Version sm_20 sm_21 sm_30 sm_32 sm_35 sm_37 sm_50 sm_52 sm_53 sm_60 sm_61 sm_62 Threads / Warp 32 32 32 32 32 32 32 32 32 32 32 32 Warps / Multiprocessor 48 48 64 64 64 64 64 64 64 64 64 128 Threads / Multiprocessor 1536 1536 2048 2048 2048 2048 2048 2048 2048 2048 2048 4096 Thread Blocks / Multiprocessor 8 8 16 16 16 16 32 32 32 32 32 32 Shared Memory / Multiprocessor (bytes) 49152 49152 49152 49152 49152 114688 65536 98304 65536 65536 98304 65536 Max Shared Memory / Block (bytes) 49152 49152 49152 49152 49152 49152 49152 49152 49152 49152 49152 49152 Register File Size / Multiprocessor (32-bit register s) 32768 32768 65536 65536 65536 131072 65536 65536 65536 65536 65536 65536 Max Registers / Block 32768 32768 65536 65536 65536 65536 65536 65536 32768 65536 65536 65536 Register Allocation Unit Size 64 64 256 256 256 256 256 256 256 256 256 256 Register Allocation Granularity warp warp warp warp warp warp warp warp warp warp warp warp Max Registers / Thread 63 63 63 255 255 255 255 255 255 255 255 255 Shared Memory Allocation Unit Size 128 128 256 256 256 256 256 256 256 256 256 256 Warp Allocation Granularity 2 2 4 4 4 4 4 4 4 2 4 4 Max Thread Block Size 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 1024 Shared Memory Size Configurations (bytes) 49152 49152 49152 49152 49152 114688 65536 98304 65536 65536 98304 65536 [note: default at top of list] 16384 16384 32768 32768 32768 98304 #N/A #N/A #N/A #N/A 16384 16384 16384 81920 #N/A #N/A Warp register allocation granularities 64 64 256 256 256 256 256 256 256 256 256 256
CUDA 프로그래밍 코딩
단순포팅예제
구현전략 싱글스레드 C코드로작성한다 자료구조상으로최적화한다. 멀티스레드로바꾼다. CUDA로 ( 단순 ) 포팅한다. 퍼포먼스를측정한다. CUDA에적합하도록일부의설계를변경한다. 최적화한다.
CPU 코드로작성 CUDA 로포팅할것을염두해두고작성한다 CUDA 6 이전에는메모리할당및카피가주요이슈였지만 CUDA 6 / Kepler 아키텍처이후부터 Unified Memory System 사용가능. 명시적으로시스템메모리 <-> GPU 메모리전송이필요없음. 일단 CPU 상에서멀쩡히돌아가는게가장중요하다. SSE,AVX 등 SIMD 최적화는나중에생각하자.CUDA 포팅에걸림돌이된다.
Multi-Thread 코드작성 잘돌아가는 Single-Thread 코드를작성했으면 Multi- Thread 버젼을만든다. 충돌처리할오브젝트들을 Thread 개수로나눠서처리.
CUDA 코드로포팅 표준적인문법만사용했다면별로수정할게없다. Multi-Thread 코드와유사하다. CPU Thread -> CUDA Thread 로바로맵핑하면된다. device 지시어만사용하지말고최대한 host device 지시어를함께지정해서 CPU 에서미리돌려볼수있도록한다.
단순포팅 처리하고하는 [ 원소 1 개 > thread 1 개 ] 직접맵핑 1 pixel -> 1 thread, 1 오브젝트 -> 1 스레드 C 로짠코드는거의 100% 그대로돌릴수있다. 이미지프로세싱매트릭스연산등에선충분히효과가있다.
CUDA 프로그래밍 CPU 프로그래밍과다른점 - GPU 는 I/O 디바이스
시스템메모리 <-> GPU 메모리 GPU 에일을시키려면 GPU 에서사용할데이터는 GPU 메모리에전송해야한다. cudamallochost() 로시스템메모리할당 cudamalloc() 으로 GPU 메모리할당 cudamemcpy() 로전송
시스템메모리 <-> GPU 메모리 GPU 에일을시키려면 GPU 에서사용할데이터는 GPU 메모리에전송해야한다. cudamallochost() 로시스템메모리할당 cudamalloc() 으로 GPU 메모리할당 cudamemcpy() 로전송 Unified Memory System cudamallocmanaged() 로할당하면자동으로처리. Page fault 의마법
CUDA 프로그래밍 최적화
Occupancy SM 당활성화 Warp 수 / SM 당최대 Warp 수 GPU 의처리능력을얼마나사용하는지척도 대부분의경우 Occupancy 가증가하면효율 ( 시간당처리량 ) 도증가함 Global Memory 억세스로 latency 가발생할때유휴 Warp 를수행함. GPU 디바이스의스펙에상관없이 WARP 를많이유지할수있으면처리성능이높아진다.
Occupancy Calculator
Occupancy 늘리기 많은워프를사용하여 Latency 숨기기 레지스터수를줄이도록노력한다. Shared Memory 사용을아낀다.( 줄이는것이아니다 ) Shared Memory 사용량때문에 Occupancy 를떨어뜨린다면 Shared Memory 대신 Global Memory 를사용하는것이나을수있다.(L2 캐쉬때문에생각보단느리지않다 )
Register 사용량줄이기 사실뾰족한방법이있는건아니다. 알고리즘수준에서의최적화 -> 결과적으로작은코드. 레지스터개수를컴파일타임에제한할수있다.
Shared Memory 아껴쓰기 자주억세스할경우 Shared Memory 에올려놓으면빠르다. 무작정마구쓰면 SM 에맵핑할수있는블럭수가크게떨어진다. Shared Memory 절약 ->Active Block 수증가 -> 성능향상
cudastream 을이용한비동기처리 CUDA 디바이스는기본적으로 Copy engine 을 2 개가지고있다. 시스템메모리 <-> GPU 메모리전송도중에도 CUDA Kernel 코드실행가능 따라서여러개의 copy 작업과연산작업을동시에처리가능 각작업의컨텍스트를유지하고순서를맞추기위해 cudastream 을사용. 여러개의 cudastream 과 Multi-Thread 와함께사용하여성능을극대화한다.
한번에많은데이터처리하기. 내부적으론 PCI 버스를타고 GPU 메모리로전송됨. CUDA Kernel 함수한번호출하고결과를받아오는작업은굉장히긴시간이필요함. 비행기탑승에걸리는시간은? 작은데이터를여러번처리하는대신모아서한번에처리한다.
데이터사이즈줄이기 CUDA Kernel 함수실행중에가장많은시간을소모하는작업은 global memory 읽기 / 쓰기 데이터사이즈가작으면 global memory 억세스가줄어듦 -> 성능향상 데이터사이즈가작으면캐시히트율이높아짐. -> global memory 억세스가줄어듦 -> 성능향상.
게임프로젝트에 CUDA 적용
GPGPU 적용판단기준 부적합한경우 분기가많다. 각요소들의의존성이높다.( 병렬화하기나쁘다 ) Throughput 보다 Responsibility 가중요하다.(100ms or 50ms 에목숨을건다!) 적합한경우 코드에서분기가적다. Image Processing 처럼각요소들의의존성이낮다. ( 병렬화하기좋다.) Throughput 이중요한경우.(3 일걸리던작업을 1 일로줄였다!!!)
게임프로젝트에 CUDA 적용 nvidia GPU 만지원하잖아 어차피게이머들 GPU 는 75% 이상 nvidia 제품. 절반이상의플레이어에게도움이됨. 에디터등개발중에만사용해도충분히쓸모가많음. 코드논리와흐름은같으므로 Compute Shader 로포팅할때도움이됨. Multi-thread 최적화에도도움이됨.
일반적으로가능한작업들 물리처리등 PhysX ( 실제로널리사용 ) 개발단계에서정적라이트계산 게임플레이중라이트계산 압축된텍스처나지형의디코딩
Rage - id soft https://www.geforce.com/what s-new/articles/how-to-unlockrages-high-resolution-textureswith-a-few-simple-tweaks
Proejct D Online Lightmap baking
서버 / 클라이언트를위한충돌처리 https://youtu.be/qhv3tvf0qxc https://youtu.be/cy2sa_dgbmc http://youtu.be/towb280qlgo http://youtu.be/egwquxvtths 성능은 2 배 -4 배정도 몇가지이유로상용서비스로의적용은하지않았음.
Voxel Horizon Lightmap baking https://youtu.be/ixvbqsw8d2a
Reference https://megayuchi.com https://developer.nvidia.com/cuda-zone