본문 바로가기

GPU-KERNEL

Register Pressure -> Warp Scheduler -> Occupancy -> (Nsight 타임라인 구조)

1. Register Pressure : 한 스레드가 많은 작업을 할수록 생기는 일

개념

  • Register pressure = 스레드당 사용하는 레지스터 개수가 많아지는 것
  • HW 는 SM 당 레지스터 총량이 한정돼 있음

 

제약 공식

SM 에서 동시에 올릴 수 있는 스레드는 다음 조건에 막힌다. 

1) regs_per_thread × threads_per_block × blocks_per_SM ≤ regs_per_SM
2) shared_mem_per_block × blocks_per_SM ≤ shared_mem_per_SM
3) blocks_per_SM ≤ HW가 허용하는 최대 블록 수
4) warps_per_SM ≤ HW가 허용하는 최대 워프 수

즉 레지스터를 많이 쓰면

  • regs_per_thread ↑ , blocks_per_SM ↓ , 동시에 돌릴 수 있는 워프 수 감소
  • 이게 바로 register pressure 가 occupancy 를 깎아먹는 경로

 

2. Warp Schedulaer : 일 시킬 워프가 충분히 있어야 한다.

현대 SM 을 아주 단순화하면

  • Warp scheduler 여러 개가 있고
  • 매 사이클마다 준비된 워프에게 명령어 한 덩어리를 발행

그래서

  • 동시에 올려져 있는 워프 수가 많을수록
    • 어떤 워프가 대기하더라도 다른 워프로 채워 넣을 수 있음
    • 이게 latency hiding

즉, 레지스터 압박이 워프 수를 줄이면 -> 스케줄러가 고를 카드가 줄어듦, latency hiding 능력이 떨어짐

 

3. Occupancy : 많이 올리면 좋지만, 100% 가 정답이 아님

Occupancy = 실제활성 워프 수 / HW 가 허용하는최대 워프 수 

최대 64 warps / SM 가능, 지금 32 warps / SM 활성 - > occupancy = 50%

 

중요 포인트

  •  100% occupancy = 항상 최고 성능이 아님
  • 보통
    • 30~50% 만 돼도 compute-heavy 커널은 잘 나옴
    • 메모리 바운드 kernel 은 occupancy 를 높여서 latency hiding 을 극대화하는 게 중요

  • reg-tiling, shared-tiling 을 해서 연산 집약도를 높이려면
    • occupancy 조금 떨어져도 성능이 오히려 더 좋아질 수 있음

 

 

4. 각 GEMM 실험에 대응해서 살펴보면

  1.  naive GEMM
    • 레지스터 사용 : 작음
    • shared memory 사용 : 거의 없음
    • occupancy  높을 가능성 큼
    • 하지만 글로벌 메모리 반복 로드 때문에 완전 memory bound
    •  SM 은 열심히 동작하지만 DRAM 대기 시간이 대부분
  2. tiled GEMM
    • shared memory tiling 추가
    • 레지스터 약간 증가, shared mem/block 증가
    • occupancy 약간 떨어질 수 있음
    • 메모리 재사용 증가, arithmetic intensity 증가
    • 결과적으로 메모리 대기 비율 감소, 성능 증가
  3. tiled + register tiling (regN)
    • 스레드당 accumulator 레지스터 여러 개
    • regs_per_thread 크게 상승 -> blocks_per_SM 제한 -> occupancy 더 하락 가능
    • 하지만
      • 한 번 가져온 데이터를 가지고 FMA 여러 번 수행
      • shared/Global 로드 대비 연산량 폭증
    • DRAM 대기 비율은 더 줄고, SM ALU/Tensor Core 사용률 증가