본문 바로가기
Program Language/OpenCL

[CUDA] occupancy

by Leo 리오 2012. 11. 7.
반응형

들어가는말,


GPU는 캐시가 없거나 아주 작다. 따라서 Global 메모리를 접근 빈도수가 높은데, latency또한 높기 때문에 GPU의 계산 속도는 엄청나게 느릴 것이다. 이것을 보안하기 위한 방법이 메모리에 접근하는 동안 실행되는 warp을 교체(context switch)해버리는 방법을 사용하게 된다.

또한, 앞뒤 instruction간의 dependency가 있을 경우도 stall을 해야하는데 이것 또한 빠른 context switch로 상쇄(hide)시킬 수 있다. (or ILP를 높임)


Fermi의 경우,

GMEM latency: 400-800 cycles

Arithmetic latency: 18-22cycles


GPU는 SM에서 context switch가 자유롭다. (overhead = 0)

[이유를 간단히 말하자면, Warp의 묶음인 block이 SM에 할당 되게 되면, 각각의 Thread들은 자신만의 자원(register, shared memory)들을 가지고 있다. 따라서 말이 context switch이지 변하는건 없다.]


결론은 SM에 Thread(block)를 많이 넣으면 넣으면 넣을 수록 성능이 좋다는것이다. (block개수)/(최대 block개수) = occupancy라고 한다.

그럼 어떻게 occupancy를 높일 수 있을까??



Geforce 550 TI


CL_DEVICE_COMPUTE_CAPABILITY_NV: 2.1
NUMBER OF MULTIPROCESSORS: 4

NUMBER OF CUDA CORES: 192
CL_DEVICE_REGISTERS_PER_BLOCK_NV: 32768
CL_DEVICE_WARP_SIZE_NV: 32
CL_DEVICE_GPU_OVERLAP_NV: CL_TRUE
CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV: CL_FALSE
CL_DEVICE_INTEGRATED_MEMORY_NV: CL_FALSE
CL_DEVICE_PREFERRED_VECTOR_WIDTH_<t> CHAR 1, SHORT 1, INT 1, LONG 1, FLOAT 1, DOUBLE 1



Physical Limits for GPU Compute Capability: 2.0


Threads per Warp 32

Warps per Multiprocessor 48

Threads per Multiprocessor 1536

Thread Blocks per Multiprocessor 8

Total # of 32-bit registers per Multiprocessor 32768

Register allocation unit size 64

Register allocation granularity warp

Registers per Thread 63

Shared Memory per Multiprocessor (bytes) 49152

Shared Memory Allocation unit size 128

Warp allocation granularity 2

Maximum Thread Block Size 1024




GPU Architecture





여러개의 SP(Stream Processor)가 합쳐서 SM(Stream MultiProcessor)를 이룬다.

SM에는 Registers, Shared Memory를 공유한다.


위의 스펙을 보면

지포스 550ti는 SM이 4개이고,

전체 SP는 192개이고,

하나의 SM당 SP는 192/4=48개가 있다.

Register는 32768개.

Shared Memory는 49152bytes있다.


이 SM에 warp의 모음인 block이 매핑 되게 된다. 

global_work_item이 32*27 이고,

local_work_item이 32*3 이면,

한 block당 thread는 32*3개, 3개의 warp이 매핑 되고,

27/3=9개의 block들이 생기게 된다.

그리고 Thread Blocks per Multiprocessor:8 이기 때문에

SM0에 8개의 block이 매핑 되고

SM1에 1개의 block이 매핑 될 수 있다.

그럼, Warps per Multiprocessor:48이고, SM에 3*8warp이 매핑 됬으므로, occupancy는 24warp/48=50%가 되게 된다.


그럼 50%를 100%로 만들 수 있는 방법은?


우선 SM당 warp수는 

[warps/block] * [block/SM] 으로 계산 가능하다.


[warps/block]은 local_work_item/32이다.


[block/SM]은

SM당 block수를 제한하는 요소는 세가지가 있다.

Thread Blocks per Multiprocessor 8

Total # of 32-bit registers per Multiprocessor 32768

Shared Memory per Multiprocessor (bytes) 49152

이 세가지 중에 가장 작은 값이 개수가 된다.


1. block

block의 수는 constant다.

maxblock/SM = 8, maxwarps/SM = 48 이기 때문에

warps/block이 최소 6(32*6threads/block)이면 된다.


2. register

커널을 컴파일 해보면 커널(thread)당 사용하는 register의 수를 알 수 있다.

만약 32개의 레지스터를 사용한다면 SM당 최대 thread의 수는 32768/20=1024개 이고 이것은 1024/32=32warps/SM이다.


3. shared memory

마찬가지로 컴파일 해보면 커널당 (thread)당 사용하는 shared memory의 양을 알 수있다.


1,2,3을 모두 만족하는 즉, 최소값이 SM당 warps수가 된다.





참고.

http://developer.download.nvidia.com/CUDA/training/NVIDIA_GPU_Computing_Webinars_Further_CUDA_Optimization.pdf

http://www.nvidia.com/content/PDF/sc_2010/CUDA_Tutorial/SC10_Fundamental_Optimizations.pdf

http://developer.download.nvidia.com/CUDA/training/cuda_webinars_WarpsAndOccupancy.pdf

http://nvidia.fullviewmedia.com/gtc2010/0922-a5-2238.html

http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf























반응형

'Program Language > OpenCL' 카테고리의 다른 글

OpenCL 에러  (0) 2013.01.21
global memory replay overhead  (0) 2012.12.21
NVIDIA clEnqueueReadBuffer non-blocking bug(?)  (0) 2012.11.02
Intel opencl platform analyzer  (0) 2012.10.15
DeviceQuery  (0) 2012.10.08

댓글