2. GPGPU(General-Purpose computing on GPU
• GPU를 사용하여 CPU가 전통적으로 취급했던 응
용 프로그램들의 계산을 수행하는 기술
• GPU 코어 1개의 효율은 CPU 코어 1개에 비해 많
이 떨어지지만 코어의 개수가 엄청나게 많다.
• 많은 수의 코어를 사용하면 산술술 연산 성능
(Throughput)은 CPU의 성능보다 훨씬 뛰어나다.
3. GPGPU의 특징
강점
• 엄청난 수의 스레드를 사용할
수 있다.
• 부동소수점 연산이 엄청 빠르
다.
• 컨텍스트 스위칭이 엄청 빠르
다.
약점
• 흐름제어 기능 자체가 빈약하
다.
• 프로그래밍 기법의 제약(재귀
호출 등)
• Core당 클럭이 CPU에 비해 많
이 느리다(1/3 – 1/2수준)
8. 성능 비교
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 완전 동일.
알고리즘상의 최적화는 없음.
병렬처리 효율의 비교를 위한 테스트임.
11. CUDA (Compute Unified Device
Architecture)
• C언어 등 산업 표준 언어를 사용하여 GPU에서 작동하
는 병렬처리 코드를 작성할 수 있도록 하는 GPGPU기
술
• nVidia가 개발,배포.그래서 nVidia GPU만 가능.
• 비슷한 기술로 OpenCL, Direct Compute Shader가 있
음.
12. CUDA의 장점
•자료가 많다.
•유일하게 GPU상에서의 디버깅이 가능하다.
•C/C++에서의 포팅이 쉽다. 포인터 사용 가
능!!!!
•경쟁 제품들이 다 사망....
14. Thread
• CPU의 Thread와 비슷…하다(같지 않다).
• GPU의 Core(혹은 SP)에 맵핑
• 독립적인 컨텍스트를 가지고 있다(라고 해봐야 연산에
필요한 컨텍스트 뿐).
• 컨텍스트 스위칭이 빠르다.
• 독립적인 흐름제어는 불가능하다. Warp(32개 스레드)
단위로 묶여서 움직임).
15. Block
• Block -> N개의 Thread집합. SM에 맵핑
• 동일 Block의 스레드는 L1 cache와 Shared Memory를 공유
한다.
• 스레드만 잔뜩 있으면 될것 같은데 왜 이런게 있냐면…그것
은 아마도
CPU의 core !≒ GPU의 core
CPU의 core ≒ GPU의 SM
이기 때문일 것이다.
21. 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()
GroupMemoryBarrie
rWithGroupSync()
언어
C/C++ ,ptr사용 가
능
HLSL, similar to C
23. 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로 사용
28. SIMT(Single Instruction Multiple Threads)
• 동일 명령어를 여러개의 Thread가 동시 실행
• GPU는 기본적으로 SIMT로 작동
• N차원 벡터를 다루는 산술처리에선 적합
• Thread중 일부가 분기를 하거나 루프를 하면 나머지
Thread들은 대기 -> 병렬처리의 의미가 없어짐.
29. WARP의 이해
• Thread들을 묶어서 스케쥴링하는 단위
• 현재까지는 모든 nVidia GPU의 1 Warp는 32 Thread.
즉 32 Thread는 늘 같은 같은 코드 어드레스를 지나감.
• 동시 수행 가능한 Warp 수는 SM의 Warp 스케쥴러 개
수에 의존. SM당 2-4개의 Warp를 동시에 실행가능
30. WARP의 이해
• 32 Thread중 1개의 Thread만이 분기해서 Loop를 돌
고 있으면 31개의 Thread는 1개의 Thread가 Loop를
마치고 같은 코드 어드레스를 수행할 수 있을 때까지
대기.
• 각 Thread가 다른 루프, 다른 흐름을 타는 것은 엄청
난 성능 저하를 부른다.
35. 구현전략
• 싱글스레드 C코드로 작성한다
• 자료구조상으로 최적화한다.
• 멀티스레드로 바꾼다.
• CUDA로 (단순) 포팅한다.
• 퍼포먼스를 측정한다.
• CUDA에 적합하도록 일부의 설계를 변경 한다.
• 최적화한다.
36. CPU코드로 작성
• CUDA로 포팅할 것을 염두해 두고 작성한다
• CUDA 6 이전에는 메모리 할당 및 카피가 주요 이슈였
지만 CUDA 6 / Kepler 아키텍처 이후부터 Unified
Memory System 사용가능. 명시적으로 시스템 메모리
<-> GPU메모리 전송이 필요없음.
• 일단 CPU상에서 멀쩡히 돌아가는게 가장 중요하다.
• SSE,AVX등 SIMD최적화는 나중에 생각하자.CUDA포팅
에 걸림돌이 된다.
37. Multi-Thread코드 작성
• 잘 돌아가는 Single-Thread코드를 작성했으면 Multi-
Thread버젼을 만든다.
• 충돌처리할 오브젝트들을 Thread개수로 나눠서 처리.
38. CUDA코드로 포팅
• 표준적인 문법만 사용했다면 별로 수정할게 없다.
• Multi-Thread코드와 유사하다. CPU Thread -> CUDA
Thread로 바로 맵핑하면 된다.
• __device__ 지시어만 사용하지말고 최대한 __host__
__device__ 지시어를 함께 지정해서 CPU에서 미리 돌
려볼수 있도록 한다.
39. 단순포팅
• 처리하고 하는 [원소 1개 > thread 1개] 직접 맵핑
• 1 pixel -> 1 thread, 1 오브젝트-> 1스레드
• C로 짠 코드는 거의 100% 그대로 돌릴 수 있다.
• 이미지 프로세싱 매트릭스 연산 등에선 충분히 효과
가 있다.
41. 시스템 메모리 <-> GPU 메모리
• GPU에 일을 시키려면 GPU에서 사용할 데이터는 GPU메모리에
전송해야한다.
• cudaMallocHost()로 시스템 메모리 할당
• cudaMalloc()으로 GPU메모리 할당
• cudaMemcpy()로 전송
42. 시스템 메모리 <-> GPU 메모리
• GPU에 일을 시키려면 GPU에서 사용할 데이터는 GPU메모리에
전송해야한다.
• cudaMallocHost()로 시스템 메모리 할당
• cudaMalloc()으로 GPU메모리 할당
• cudaMemcpy()로 전송
Unified Memory System
cudaMallocManaged()로 할당하면 자동으로 처리.
Page fault의 마법
44. Occupancy
• SM당 활성화 Warp 수 / SM당 최대 Warp 수
• GPU의 처리능력을 얼마나 사용하는지 척도
• 대부분의 경우 Occupancy가 증가하면 효율(시간당 처
리량)도 증가함
• Global Memory 억세스로 latency가 발생할때 유휴
Warp를 수행함.
• GPU 디바이스의 스펙에 상관없이 WARP를 많이 유지
할 수 있으면 처리성능이 높아진다.
46. Occupancy늘리기
• 많은 워프를 사용하여 Latency 숨기기
• 레지스터 수를 줄이도록 노력한다.
• Shared Memory사용을 아낀다.(줄이는 것이 아니다)
• Shared Memory사용량 때문에 Occupancy를 떨어뜨
린다면 Shared Memory대신 Global Memory를 사용
하는 것이 나을수 있다.(L2캐쉬 때문에 생각보단 느리
지 않다)
48. Register 사용량 줄이기
• 사실 뾰족한 방법이 있는건 아니다.
• 알고리즘 수준에서의 최적화 -> 결과적으로 작은 코
드.
• 레지스터 개수를 컴파일 타임에 제한할 수 있다.
49. Shared Memory아껴쓰기
• 자주 억세스할 경우 Shared Memory에 올려놓으면 빠
르다.
• 무작정 마구 쓰면 SM에 맵핑할 수 있는 블럭 수가 크
게 떨어진다.
• Shared Memory절약 ->Active Block수 증가 -> 성능
향상
50. cudaStream을 이용한 비동기 처리
• CUDA디바이스는 기본적으로 Copy engine을 2개 가지고 있다.
• 시스템 메모리 <-> GPU메모리 전송 도중에도 CUDA Kernel코
드 실행 가능
• 따라서 여러 개의 copy 작업과 연산 작업을 동시에 처리 가능
• 각 작업의 컨텍스트를 유지하고 순서를 맞추기 위해
cudaStream을 사용.
• 여러 개의 cudaStream과 Multi-Thread와 함께 사용하여 성능을
극대화한다.
51. 한번에 많은 데이터 처리하기.
• 내부적으론 PCI버스를 타고 GPU메모리로 전송됨.
• CUDA Kernel함수 한번 호출하고 결과를 받아오는 작
업은 굉장히 긴 시간이 필요함. – 비행기 탑승에 걸리
는 시간은?
• 작은 데이터를 여러 번 처리하는 대신 모아서 한번에
처리한다.
52. 데이터 사이즈 줄이기
• CUDA Kernel함수 실행중에 가장 많은 시간을 소모하
는 작업은 global memory 읽기/쓰기
• 데이터 사이즈가 작으면 global memory억세스가 줄
어듦 -> 성능향상
• 데이터 사이즈가 작으면 캐시 히트율이 높아짐. ->
global memory 억세스가 줄어듦 -> 성능 향상.
54. 부적합한 경우
• 분기가 많다.
• 각 요소들의 의존성이 높
다.(병렬화 하기 나쁘다)
• Throughput보다
Responsibility가 중요하
다.(100ms or 50ms에 목숨을
건다!)
적합한 경우
• 코드에서 분기가 적다.
• Image Processing처럼 각
요소들의 의존성이 낮다.
– (병렬화 하기 좋다.)
• Throughput이 중요한 경
우.(3일걸리던 작업을 1일
로 줄였다!!!)
GPGPU 적용 판단 기준
55. 게임프로젝트에 CUDA적용
• nvidia GPU만 지원하잖아
• 어차피 게이머들 GPU는 75%이상 nvidia제품. 절반 이상의 플레이어에
게 도움이 됨.
• 에디터등 개발중에만 사용해도 충분히 쓸모가 많음.
• 코드 논리와 흐름은 같으므로 Compute Shader로 포팅할때 도움이 됨.
• Multi-thread 최적화에도 도움이 됨.
56. 일반적으로 가능한 작업들
• 물리처리등– PhysX (실제로 널리 사용)
• 개발 단계에서 정적 라이트 계산
• 게임 플레이중 라이트 계산
• 압축된 텍스처나 지형의 디코딩
57. Rage - id soft
• https://www.geforce.com/what
s-new/articles/how-to-unlock-
rages-high-resolution-textures-
with-a-few-simple-tweaks
59. 서버/클라이언트
를 위한 충돌처리
• https://youtu.be/QHv3tVF0qxc
• https://youtu.be/cY2sa_dGbMc
• http://youtu.be/towb280Qlgo
• http://youtu.be/egWQUxvTThs
• 성능은 2배-4배 정도
• 몇가지 이유로 상용서비스로의 적용
은 하지 않았음.