SINGLE-BOOK COMPRESSED SHEET · A4 LANDSCAPE · 17p

PMPP 단권화

Programming Massively Parallel Processors (4e) — Ch 1~14 핵심 + Ch 15·16·20 압축
원서 PMPP 4e (Hwu, Kirk, Hajj)
형식 A4 가로 · 3열
기종 기준 NVIDIA A100 (cf. H100)
목표 시험장 펼침 / 밀도 ≥80%

목차

1. Introduction + Amdahlp.1
2. Hetero Data Parallel (CUDA C)p.2
3. Multi-D Grids & Indexingp.3
4. Compute Arch: Warp / SIMTp.4
4. Scheduling · Divergence · Occupancyp.5
5. Memory Hierarchyp.6
5. Tiling (Shared Memory)p.7
6. Coalescing · Bank Conflictp.8
7. Convolutionp.9
8·9. Stencil · Histogramp.10
10. Reductionp.11
11. Prefix Sum (Scan)p.12
12·13. Merge · Sortp.13
14+ Sparse · 요약 체크리스트p.14
15. Graph Traversal (BFS)p.15
16. Deep Learning (CNN)p.16
20. MPI + CUDA (Multi-GPU)p.17

범례

핵심 용어 (노란 배경)
표 헤더 / 매우 중요
정의·공식 박스
예시·워크드 박스
빨강주의·실수하기 쉬움
시험·실무 핵심
(!)니모닉 (첫글자 암기)
인과·흐름
↑↓증가·감소
∵∴이유·결론
인쇄 설정 · A4 가로 / 여백 없음 / 배경 그래픽 포함 · Ctrl(⌘)+P
Ch 1~14 + 15·16·20 · 17 pages

1 Heterogeneous Parallel Computing

정의 Heterogeneous: CPU + GPU(or FPGA/Accel) sequential부분은 CPU, data-parallel부분은 GPU.
  • 2003 이후 clock ↑ 벽 (열·전력) multi-core 전환
  • 두 설계 trajectory:
    • Multi-core: sequential 성능 유지 (Intel 24-core 등)
    • Many-thread: throughput 극대화 (NVIDIA GPU)
  • von Neumann 모델 · thread = 순차 실행 단위

2 CPU vs GPU 설계 철학

항목CPU (latency-oriented)GPU (throughput-oriented)
목표단일 thread 지연 ↓총 throughput ↑
ALU소수·대형·저지연다수·소형·장지연 허용
캐시큰 L1/L2/L3작음 (대역폭 관리용)
제어분기예측·OoO 복잡단순 in-order pipeline
메모리legacy 일관성relaxed, ~10× 대역폭
쓰레드수~수십수만
latency ↓ 비용 ≫ throughput ↑ 비용
∵ latency 반감 → 전류·면적 ×4 / throughput ×2 → 면적·전력 ×2

3 A100 성능 수치 기준 기종

지표A100CPU(24-c)
FP64 peak9.7 TF0.33 TF
FP32 peak156 TF0.66 TF
FP16 peak312 TF
대역폭~1.5 TB/s~0.2 TB/s

cf. H100 FP64 ~34 TF (SM 수↑, TMA, async copy)

4 Amdahl's Law ★ 병렬화 상한 s·p·1

Speedup(p, s) = 1 / ((1 − p) + p/s) p : 병렬 가능 비율 ∈[0,1]   s : 병렬부 speedup   s→∞ : Speedup_max = 1/(1−p)
p=0.30, s=100 1/(0.70 + 0.003) ≈ 1.42× (상한 1.43)
p=0.99, s=100 1/(0.01 + 0.0099) ≈ 50× (상한 100)
∴ p를 99%+로 끌어올리는 것이 s를 키우는 것보다 중요
  • sequential 부분이 peach pit · parallel 부분이 flesh
  • straightforward parallel → DRAM bandwidth saturate → ~10× 수준
  • on-chip memory 활용 최적화 → 100×+ 가능

5 Speed 수요 동기

  1. Scientific simulation (bio molecule, climate)
  2. Video/Image processing (HDR, view synth)
  3. UI · computer vision · 음성
  4. Deep Learning ★ : 대규모 labeled data + GPU throughput
  5. Digital twin · realistic physics

6 병렬 프로그래밍 4대 난제

  1. Algorithmic complexity: parallel = more work 가능 work efficiency 확보
  2. Memory-bound 다수 Ch5, Ch6 최적화
  3. Input-sensitive: 분포 불균형 uneven work
  4. Synchronization 필요 ↔ embarrassingly parallel

7 Programming Interfaces 비교

모델범위특징
CUDAsingle node GPUNVIDIA 전용 · explicit control · C/C++ ext
OpenCL여러 vendorCUDA와 개념 유사 · API 위주
OpenMPshared-mem multi-corepragma 기반 · GPU 확장 중
MPIcluster, 100k+ nodemessage passing · no shared mem

HPC 실무: MPI + CUDA 혼합 (multi-GPU · NCCL) — Ch20

8 CUDA 등장 의의 2007

  • G80: GPGPU API(OpenGL/D3D) 범용 parallel 인터페이스
  • 그래픽 pixel 제약 제거 · C/C++로 직접
  • 설치 기반 >10억대 · 경제성 확보

9 책의 3대 목표

  1. Performance: GPU HW 이해 기반 최적화
  2. Correctness: barrier·atomic·memory consistency
  3. Scalability: 차세대 HW 확장성
CPU/GPU 차이 6키: 목ALU캐제메쓰 (목표·ALU·캐시·제어·메모리·쓰레드)

10 책 구성 4부

  1. Part I: 기초 (Ch2~6)
  2. Part II: primitive patterns (Ch7~12)
  3. Part III: advanced patterns (Ch13~19)
  4. Part IV: MPI+CUDA, dynamic parallelism (Ch20~21)

1 Data Parallelism 정의

정의 데이터의 독립적 부분들에 동일 연산 적용 SPMD (Single Program Multiple Data) 모델.
  • 예: vector add · 이미지 픽셀 변환 · 행렬곱
  • task parallelism과 구분: 서로 다른 task 동시 실행

2 CUDA 프로그램 구조 Host + Device 할복실복해

  1. 할당: cudaMalloc(&d_A, size)
  2. 복사 H→D: cudaMemcpy(d_A, h_A, n, H2D)
  3. 실행: kernel<<<grid, block>>>(...)
  4. 복사 D→H: cudaMemcpy(h_A, d_A, n, D2H)
  5. 해제: cudaFree(d_A)
CUDA 5단계: 할복실복해 (할당·복사·실행·복사·해제)

3 함수 한정자

한정자호출실행
__host__hosthost
__global__host(&dyn.)device
__device__devicedevice

__global__ 반드시 void return

4 Vector Add 커널 ★ i = bx·bD + tx

__global__ void vecAdd(float* A, float* B, float* C, int n){
  int i = blockIdx.x*blockDim.x + threadIdx.x;
  if(i < n) C[i] = A[i] + B[i];
}
// host launch
int T=256, B=(n+T-1)/T;
vecAdd<<<B,T>>>(d_A,d_B,d_C,n);
if(i<n) boundary check 필수 — n이 blockDim 배수가 아니면 OOB.

5 Kernel 실행 구성 <<<G,B>>>

  • grid: block의 집합 (최대 3D)
  • block: thread의 집합 (최대 3D, ≤1024 thr)
  • block 간 독립 실행 — 동기화 불가
  • block 내부: __syncthreads(), shared mem OK
grid[ block(0,0) block(1,0) ... ]
 └─ block[ t(0,0) t(1,0) ... t(31,0)  ← warp
            t(0,1) ...                ]

6 내장 변수

변수의미
gridDim.{x,y,z}grid 크기 (block 수)
blockDim.{x,y,z}block 크기 (thread 수)
blockIdx.{x,y,z}block의 grid 내 좌표
threadIdx.{x,y,z}thread의 block 내 좌표

7 cudaError 처리

cudaError_t e = cudaMalloc(&d,n);
if(e != cudaSuccess){
  printf("%s\n",cudaGetErrorString(e));
  exit(-1);
}

실무 매크로: CUDA_CHECK(x) 래퍼 권장

8 Unified / Pinned / Streams 간략

  • cudaMallocManaged: UM, page fault로 자동 migrate
  • cudaMallocHost: pinned host mem H↔D 대역폭 ↑
  • cudaStream_t: 비동기 복사/커널 겹침 (H2D || kernel || D2H)

9 워크플로 체크리스트

  1. host 입력 준비
  2. device 메모리 할당 크기 = N·sizeof(T)
  3. H→D 복사
  4. 그리드 차원 = ⌈n/T⌉ · boundary if
  5. 커널 오류 check + cudaDeviceSynchronize()
  6. D→H 복사 / cudaFree
흔한 실수: host 포인터를 커널에 직접 넘기면 segfault. 반드시 device 포인터 사용.
흔한 실수: cudaMemcpy 방향 플래그 혼동 (cudaMemcpyHostToDevice vs DeviceToHost).

1 2D/3D 그리드 왜?

  • 이미지·행렬·볼륨 데이터 = 다차원 자연스러운 mapping
  • threadIdx·blockIdx 모두 (x,y,z) 사용 가능
  • 커널 내부 인덱스 계산을 명확·오류↓하게
dim3 block(16,16);
dim3 grid((W+15)/16,(H+15)/16);
kernel<<<grid,block>>>(...);

2 2D 인덱스 공식 ★ row·col

row = blockIdx.y·blockDim.y + threadIdx.y
col = blockIdx.x·blockDim.x + threadIdx.x
idx = row·W + col  (row-major, W=너비)
혼동 주의: .xcol(너비축), .yrow(높이축).

3 Row-major 선형화

M[r][c] = M_flat[r·W + c]   (2D)
V[d][r][c] = V_flat[d·(H·W) + r·W + c]   (3D) C/CUDA는 row-major · Fortran/MATLAB은 col-major

4 ColorToGrayscale 예 ★

__global__ void rgb2gray(uchar* out, uchar* in, int W, int H){
  int c = blockIdx.x*blockDim.x + threadIdx.x;
  int r = blockIdx.y*blockDim.y + threadIdx.y;
  if(r<H && c<W){
    int i = r*W + c;
    uchar* p = &in[3*i];
    out[i] = 0.21f*p[0]+0.72f*p[1]+0.07f*p[2];
  }
}
  • grid = ⌈W/16⌉ × ⌈H/16⌉
  • block 16×16 = 256 thread (warp 8개)

5 행렬곱 naive baseline

__global__ void matMul(float* C,float* A,float* B,int N){
  int r = blockIdx.y*blockDim.y+threadIdx.y;
  int c = blockIdx.x*blockDim.x+threadIdx.x;
  if(r<N && c<N){
    float s=0;
    for(int k=0;k<N;++k)
      s += A[r*N+k]*B[k*N+c];
    C[r*N+c]=s;
  }
}
compute/memory ratio low — 매 k마다 DRAM 2 load. Ch5 tiling 필요.

6 block 크기 선택 경험칙

  • 16×16 = 256 or 32×32 = 1024 (max)
  • thread 수 = 32의 배수 (warp 단위)
  • 너무 작음 → occupancy↓ / 너무 큼 → 레지스터 부족

7 경계 처리 패턴 if r<H && c<W

  1. 내부 thread: 실제 계산
  2. 초과 thread: early return
  3. grid = ⌈N/B⌉ · 올림 division

8 3D 예 volume

dim3 blk(8,8,8);
dim3 grd((W+7)/8,(H+7)/8,(D+7)/8);
i = z*(H*W) + y*W + x;

MRI·CT·유체 시뮬에 사용

W=1920, H=1080, block=16×16 → grid = 120×68 = 8160 blocks, 총 thread ≈ 2.09M

1 GPU 계층 ★ 전체 구조

GPU
 └─ SM (Streaming Multiprocessor) × N
     ├─ warp scheduler × 2~4
     ├─ SP/CUDA core × 64~128
     ├─ Tensor Core × 4
     ├─ Register File (64K × 32b)
     ├─ Shared Mem / L1 (128~192 KB)
     └─ LD/ST, SFU
SMCUDA core
A1001086912
H10013216896

2 Warp 정의32 thread · SIMT

정의 Warp = 32 thread 한 묶음, SM이 lock-step 실행. thread들은 같은 PC·서로 다른 데이터 → SIMT.
  • block → warp 단위로 자동 분할 (x축 먼저)
  • warp = HW 스케줄링 단위 (scalar thread ×)
  • SIMT ≈ SIMD + 개별 thread 제어 흐름 허용

3 SIMT vs SIMD

항목SIMDSIMT
레인벡터 폭 고정thread 마다 ID
분기마스크 프로그램HW가 자동
메모리aligned gatherthread별 주소
모델dataSPMD

4 Control Divergence ★ 발산

정의 같은 warp 내 thread가 서로 다른 경로 → path별 순차 실행 + 반대편 thread mask throughput ↓.
if(tid % 2 == 0)  // warp 내부 ½ mask
  even_work();
else
  odd_work();   // 두 경로 직렬화
분기 조건을 warp 경계에 정렬: if(tid/32 ...) → divergence 없음.

5 분기 회피 패턴

  1. 조건 산술화: y = c ? a : by = a*c + b*(1-c)
  2. 경계 조건은 마지막 block에서만 검사
  3. warp당 uniform branch 유지

6 Latency Hiding ★

원리 한 warp가 메모리 대기 scheduler가 다른 ready warp 실행. 충분한 warp ⇒ long-latency 은폐.
#warps ≥ latency / throughput 예: 400 cycle DRAM / 1 op/cycle → ≥ 400 warp가 필요하진 않고, SM당 active warp 수로 은폐

7 Transparent Scalability

  • block 간 실행 순서·동시성 가정 ×
  • 같은 바이너리가 더 많은 SM 가진 차세대 GPU에서 자동으로 빨라짐
  • ∴ block 간 sync가 금지된 이유
Compute arch 5핵심: SWDLT (SM · Warp · Divergence · Latency hiding · Transparent scale)

1 Warp Scheduling

  • 각 SM = multi warp scheduler (A100: 4개)
  • 매 cycle: ready warp 중 선택
  • issue stall 원인: memory wait · dependency · barrier
cycle: w0┐ w1┐ w2┐ w0┐
            │   │   │   │
          issue issue issue issue
        (scoreboard-free warps rotate)

2 __syncthreads() ★

정의 block 내 모든 thread가 이 지점에 도달 전에는 아무도 지나갈 수 없음. block 간엔 효과 없음.
if 문 안쪽에 두면 안 됨 — 일부 thread만 도달 시 deadlock. 조건을 uniform하게 맞춘 뒤 호출.

3 Atomic Ch9·10 대비

  • atomicAdd / Max / CAS
  • read-modify-write 원자성 보장
  • 동일 주소 경합 → 직렬화 (throughput ↓)
  • shared mem atomic이 global보다 빠름

4 Occupancy ★ active / max

Occupancy = active_warps / max_warps_per_SM A100: max_warps/SM = 64 (2048 thread)
block=256(8warp), SM당 4 block → 32 active warp → Occupancy = 32/64 = 0.50

5 Occupancy 제약 4요소 B·T·R·S

자원A100 /SM영향
Blocks32block 수 상한
Threads2048warp 수 상한
Regs65536thread당 레지스터
Smem164 KBblock당 shared
Occupancy 제약: BTRS (Block·Thread·Reg·Shared)

6 Occupancy ≠ 성능

  • 높은 occupancy = latency hiding 여력
  • 너무 높이면 레지스터 spill → 오히려 ↓
  • ILP(instruction-level) / MLP로 보완 가능
목표는 충분한 occupancy (예: 50%+), 100% 아님.

7 튜닝 체크리스트

  1. block size = 128~512 에서 탐색
  2. -Xptxas -v로 레지스터/smem 확인
  3. __launch_bounds__로 컴파일러 힌트
  4. Nsight Compute로 stall reason 분석

8 Volatile / Fence

  • __threadfence(): 메모리 쓰기 가시성 보장
  • volatile: 레지스터 캐시 금지 (polling시)
  • Independent Thread Scheduling (Volta+) → __syncwarp() 필수

1 메모리 계층표 ★★ 필수 암기 전상지공레로

종류범위속도크기선언
Globalgrid·host~400 cyc40~80 GB__device__
Constantdevice ROL1 캐시64 KB__constant__
LocalthreadGlobal 동일thread별auto (spill)
Sharedblock~30 cyc164 KB/SM__shared__
Registerthread1 cyc65K × 4B/SMauto
Texturegrid ROspatial cachetex API
메모리 6종: 전상지공레텍 (전역·상수·지역·공유·레지스터·텍스처)    EN: GCLSRT

2 Local ≠ 가깝다 ⚠

Local memory는 물리적으로 DRAM. "지역"은 범위만 의미. 레지스터 spill·큰 배열 auto 변수 → local로 떨어짐.

3 Shared Memory 패턴

__global__ void k(...){
  __shared__ float s[256];
  int t = threadIdx.x;
  s[t] = d_in[...];        // load
  __syncthreads();
  // 여러 thread가 s[] 공유 사용
}
  • block 내 thread 간 공유·재사용
  • 동적 크기: extern __shared__ float s[];
  • 32 bank로 분할 (4B stride)

4 Constant Mem 작고 자주 읽는 것

  • 64 KB, 모든 thread 같은 값 읽기 → 1 cycle broadcast
  • 필터 계수 · 상수 테이블
  • 동일 warp가 다른 주소 읽으면 직렬화
__constant__ float F[25];
cudaMemcpyToSymbol(F, h_F, sizeof(F));

5 Register vs Spill

  • thread당 255 reg 상한 (HW)
  • 초과 시 → local(DRAM) spill ⚠
  • __launch_bounds__(T, minBlocks)로 상한 지시

6 Compute/Memory Ratio ★ CGMA / AI

CGMA = FLOP / Byte_DRAM Arithmetic Intensity라고도. 높을수록 compute-bound에 유리.
FP32 naive matmul: 2 FLOP / 2×4B = 0.25 FLOP/B
A100 BW 1.5 TB/s → 0.375 TFLOPS << 156 TF peak.
∴ memory-bound → tiling으로 CGMA ↑ 필수.

7 Roofline 요약

GFLOPS
  ▲          _______ peak compute
  │        /
  │      /  BW·AI line
  │    /
  └─────────────────▶ AI (FLOP/B)
     ↑knee = peak/BW

AI < knee → memory-bound · AI ≥ knee → compute-bound

★ 대원칙: DRAM 방문은 비싸다 → 재사용을 shared/register에서 최대화.

1 Tiling 핵심 ★★

정의 큰 행렬을 TILE_WIDTH × TILE_WIDTH 블록으로 쪼개 shared mem에 올려 재사용 → DRAM 접근 ÷ TILE_WIDTH배 감소.
  1. Phase: A·B 타일 shared mem에 로드 (협력 로딩)
  2. __syncthreads()
  3. shared에서 내적 부분합
  4. __syncthreads() · 다음 타일

2 효과 분석

DRAM loads/thr = 2·N / TILE  (naive: 2N)
CGMA = TILE   (FLOP/B 비례 ↑) TILE=16 → 16× 재사용 → bandwidth 요구 1/16
N=1024, TILE=16: DRAM access 2·1024 → 128 (thread당), compute 동일.

3 Tiled MatMul 커널 ★

#define T 16
__global__ void mm(float* C,float* A,float* B,int N){
  __shared__ float As[T][T], Bs[T][T];
  int bx=blockIdx.x, by=blockIdx.y;
  int tx=threadIdx.x, ty=threadIdx.y;
  int r=by*T+ty, c=bx*T+tx;
  float s=0;
  for(int ph=0; ph<N/T; ++ph){
    As[ty][tx]=A[r*N + ph*T+tx];   // coalesced
    Bs[ty][tx]=B[(ph*T+ty)*N + c];
    __syncthreads();
    for(int k=0;k<T;++k) s += As[ty][k]*Bs[k][tx];
    __syncthreads();
  }
  C[r*N+c]=s;
}

4 타일 크기 trade-off

TILEsmem/blockCGMAOccupancy
80.5 KB8높음
162 KB16좋음
328 KB32낮음

5 Boundary Tile 처리

  • N이 TILE 배수가 아닌 경우
  • out-of-range 위치 → 0.0f 로드 (padding)
  • 출력 쓰기 전 if(r<N && c<N) 검사
if(r<N && ph*T+tx<N)
  As[ty][tx]=A[r*N+ph*T+tx];
else As[ty][tx]=0.0f;

6 Thread Coarsening

정의 한 thread가 출력 여러 개(예: 2×2) 담당 → shared mem 재사용 ↑, 중복 로드 ↓.
  • 레지스터 많이 씀 → occupancy ↓ 주의
  • Volta+ tensor core 매트릭스에서 자연스러움
Tiling 4단계: 로싱크계싱크 (로드·sync·계산·sync)
중복 호출 주의: 두 번째 __syncthreads() 빼면 다음 phase에서 아직 쓰이는 As/Bs를 덮어써 race condition.

1 Coalesced Access ★★ warp → 1 transaction

정의 같은 warp(32 thread)가 연속·정렬 주소를 접근 → HW가 하나의 128 B 트랜잭션으로 묶음.
✓ coalesced:  t0→a[0], t1→a[1], ... t31→a[31]
✗ strided:    t0→a[0], t1→a[N], ...        (N transactions)
✗ random:     t0→a[?]                      (최대 32 trans)

2 행·열 우선 차이

접근row-major A[r][c]결과
tid→cA[r][tid]✓ coalesced
tid→rA[tid][c]✗ strided
전치·matmul의 B 접근이 strided 될 때 → shared mem transpose로 해결.

3 Bank Conflict ★ Shared Memory 32 banks · 4B

정의 shared mem = 32 bank (stride 4B). 동일 bank에 서로 다른 주소 동시 접근 → 직렬화(N-way conflict).
단, 같은 주소 → broadcast (OK).
s[tid] → 서로 다른 bank, OK
s[2*tid] → 2-way conflict (stride 2)
s[32*tid] → 32-way (완전 serialize)
해법: padding (열 32→33) · swizzle · 주소 재매핑.

4 Memory Latency Hiding 복기

필요 parallelism = BW × latency Little's law. A100: 1.5 TB/s × 400 ns ≈ 600 KB in-flight

5 성능 최적화 체크리스트 ★ 병코재분레

  1. 렬성 충분? (warp ↑)
  2. alesced load/store
  3. 사용 via shared/register (tiling)
  4. 기 발산 최소화 (uniform warp)
  5. 지스터·shared 균형 (occupancy)
GPU 튜닝 5: 병코재분레 (Parallelism · Coalesce · Reuse · Divergence · Resource)

6

  • nvprof (구) / Nsight Compute: kernel 프로파일
  • Nsight Systems: 타임라인 (stream 겹침)
  • cuda-memcheck: OOB, race

7 흔한 실수 top 5

  1. H→D 복사 누락 / 방향 플래그
  2. boundary if 빠짐 → OOB
  3. __syncthreads() 분기 안쪽
  4. shared 초기화 race (sync 없음)
  5. strided global access → bandwidth 낭비

1 1D/2D Convolution 정의

y[i] = Σk=−R..R x[i+k]·F[k]
Y[r][c] = ΣΣ X[r+i][c+j]·F[i][j] R = radius = (kernel_size − 1)/2
  • F = filter/mask (작고 재사용)
  • 출력 각 점은 주변 R개와 가중합

2 Naive 커널

__global__ void conv1d(float* y,float* x,float* F,int N,int R){
  int i=blockIdx.x*blockDim.x+threadIdx.x;
  if(i>=N) return;
  float s=0;
  for(int k=-R;k<=R;++k){
    int j=i+k;
    if(j>=0 && j<N) s += x[j]*F[k+R];
  }
  y[i]=s;
}

3 3가지 최적화 단계 ★

  1. Constant mem에 filter 저장 (brodcast cache)
  2. Tiled + halo: block이 입력 타일 + R 테두리 로드 (shared)
  3. L2 cache 의존: 최신 GPU는 halo도 L2 hit — 단순 tile + constant filter

4 Halo 패턴 그림

┌──────────── tile+halo ────────────┐
│  halo ▒▒▒▒▒▒▒  내부 tile  ▒▒▒▒▒▒▒ │
│       ↑R cells              R cells↑│
└──────────────────────────────────┘
smem 크기 = (TILE + 2R)
halo thread들이 내부 thread보다 먼저 완료 → 반드시 __syncthreads() 뒤 계산.

5 경계 처리

  • zero-padding (기본)
  • mirror (영상) / clamp (에지 복제)
  • wrap (circular)

6 CGMA 개선 2D 5×5 예

Naive: 25 FLOP / 25·4B = 0.25 FLOP/B
Tiled(16×16 + halo): DRAM load 20×20 = 400 / 256 out ≈ 1.56 per out
→ CGMA ≈ 25 / 6.25 = 4.0 FLOP/B

7 Separable Convolution

정의 2D filter F = f_row ⊗ f_col (분리형, 예: Gaussian) 2개의 1D pass로 O(K²)→O(2K) 연산.
Conv 최적화 3: 상타할 (수 filter · 일링 · 로)

1 Stencil Ch8

정의 격자 점이 자신 + 이웃만 참조해 시간 step을 진행 (PDE, 유체, 열).
7-point (3D), 5-point (2D) 등.
u_new[i,j] = c0·u[i,j] + c1·(u[i±1,j] + u[i,j±1])

2 Tiled Stencil 패턴

  • Conv와 동일하게 halo 포함 타일 로드
  • thread당 2~4개 출력(coarsening)으로 smem 재사용↑
  • 3D는 z축 slab 순회로 smem 절약
z0 slab → z1 slab (register reuse)
 ┌──────┐
 │ +R   │   smem: xy-plane
 │ core │   reg : z-1, z, z+1
 │ +R   │
 └──────┘

3 안정성·경계

  • CFL 조건: Δt ≤ Δx²/(2D) (diffusion)
  • halo를 Dirichlet/Neumann로 초기화
  • double buffer (u_old ↔ u_new) 필수

4 Histogram Ch9

정의 각 입력을 bin으로 분류해 카운트 증가. race condition 필연 atomic 필요.
__global__ void histNaive(int* h,uchar* x,int N){
  int i=blockIdx.x*blockDim.x+threadIdx.x;
  if(i<N) atomicAdd(&h[x[i]], 1);
}
skew 입력에 global atomic contention → 수십× 저하.

5 Privatization ★ 사프머

  1. block마다 shared mem private hist 소유
  2. thread들이 smem atomic으로 카운트 (빠름)
  3. __syncthreads()
  4. block별 결과 → global atomic merge
Hist 4단계: 사프머 (사본(private)·프로세스·머지)

6 Privatized Hist 커널

__global__ void histP(int* h,uchar* x,int N,int B){
  __shared__ int s[256];
  for(int t=threadIdx.x;t<B;t+=blockDim.x) s[t]=0;
  __syncthreads();
  for(int i=blockIdx.x*blockDim.x+threadIdx.x; i<N; i+=gridDim.x*blockDim.x)
    atomicAdd(&s[x[i]], 1);
  __syncthreads();
  for(int t=threadIdx.x;t<B;t+=blockDim.x)
    atomicAdd(&h[t], s[t]);
}

7 Aggregation 추가

  • thread 로컬 카운터로 연속 동일 bin 적재
  • bin이 바뀔 때만 atomicAdd
  • 이미지 공간 지역성이 클수록 효과 ↑
bin이 너무 크면(예: 65536) shared에 안 들어감 → block별 부분 bin 또는 2-pass 분리.

1 Reduction 정의

정의 배열 전체를 결합연산(+, max, min, ⊕)으로 단일 값으로 축소.
결합법칙·교환법칙이 있어야 tree-reduction 가능.
work = O(N), depth = O(log N) 이상적으로 work-efficient, log-depth

2 Naive tree (분기 ↓)

__shared__ float s[BS];
s[tid]=input[i]; __syncthreads();
for(int st=1; st<blockDim.x; st*=2){
  if(tid % (2*st)==0)      // ❌ divergence ↑
    s[tid] += s[tid+st];
  __syncthreads();
}
% 조건 → warp 내 절반씩 꺼져 divergence ↑.

3 개선 ① Contiguous thread ★

for(int st=blockDim.x/2; st>0; st>>=1){
  if(tid < st)
    s[tid] += s[tid+st];       // ✓ warp 경계 uniform
  __syncthreads();
}
  • 활성 thread가 warp의 앞쪽에 몰림 → divergence ↓
  • bank conflict 없음 (stride=1)

4 개선 ② thread당 여러 원소

  • load 단계에서 s[tid] = in[i] + in[i+BS] (2개 합산 후 저장)
  • → grid 크기 ½, global load bandwidth ↑

5 개선 ③ warp-level primitives

// 마지막 warp (tid<32) unroll
if(tid<32){
  v = s[tid] + s[tid+32];
  for(int o=16; o>0; o/=2)
    v += __shfl_down_sync(0xffffffff, v, o);
  if(tid==0) out[blk] = v;
}

__shfl_down_sync: shared mem 없이 warp 내 교환 → 레지스터만 사용.

6 전체 Reduction 흐름

  1. block-level partial sum → global[blk]
  2. 두 번째 kernel로 다시 reduce (작아짐)
  3. 또는 atomicAdd(&out[0], blockSum)
  4. CUB/thrust의 DeviceReduce 사용 권장

7 수치 주의

  • 부동소수는 결합법칙 성립X → 동일 입력·다른 순서 = 다른 값
  • Kahan / pairwise 합산으로 오차 ↓
  • FP16 합산은 overflow 위험 → FP32 accumulator
Reduction 최적화 4: 근짝워라 (접 thread · 지어 로드 · warp shuffle · 이브러리)

1 Scan 정의 ★

정의 Inclusive: y[i] = x[0]⊕..⊕x[i]
Exclusive: y[i] = x[0]⊕..⊕x[i−1], y[0] = id
x = [3, 1, 7, 0, 4, 1, 6, 3]
inclusive = [3, 4, 11, 11, 15, 16, 22, 25]
exclusive = [0, 3, 4, 11, 11, 15, 16, 22]
  • stream compaction · radix sort · sparse 연산의 기반

2 Kogge-Stone work = N log N

for(int o=1; o<BS; o*=2){
  float v=0;
  if(tid>=o) v=s[tid-o];
  __syncthreads();
  if(tid>=o) s[tid]+=v;
  __syncthreads();
}

depth log N · 병렬 ↑ · 중복 작업 많음

3 Brent-Kung ★ work-efficient up·down

  1. Up-sweep: pairwise 합을 binary tree 위로 (reduction)
  2. 최상단 원소를 identity로 초기화
  3. Down-sweep: 좌우 합을 교환·누적하며 내려옴
work = O(N), depth = O(log N) Kogge-Stone보다 work ↓ (≈ 2N)

4 Segmented / Large Scan

  1. block 내부 scan (shared)
  2. 각 block 총합을 수집 → aux[]
  3. aux 자체를 scan
  4. block i의 모든 원소에 aux[i−1] 더함
┌ blk0 ┐┌ blk1 ┐┌ blk2 ┐
 local   local   local
   Σ0      Σ1      Σ2
   └──── scan aux ───┘
   +0      +Σ0     +Σ0+Σ1

5 응용 ★

  • Stream compaction: flag[i]∈{0,1} scan → 밀집 배열의 출력 index
  • Radix sort: bit별 scan으로 재배치
  • CSR build: row 길이 → row_ptr (exclusive scan)
  • quicksort partition · histogram CDF

6 Scan 알고리즘 비교

알고리즘workdepth비고
SequentialNN기준
Kogge-StoneN log Nlog N단순
Brent-Kung2N2 log N권장
결합성 필수. 실수 합의 경우 순서 바뀌면 값 미세 변화 → 재현성 주의.
Scan 키워드: 인엑업다 (clusive · clusive · -sweep · 운-sweep)

1 Parallel Merge Ch12

정의 두 정렬 배열 A(|A|=m), B(|B|=n) → 정렬된 C(|C|=m+n).
핵심: co-rank 함수로 각 thread가 담당할 (i,j) 시작점을 binary search로 찾음.
  1. thread k의 출력 구간 [k·CEIL .. (k+1)·CEIL)
  2. co-rank(k) → (i, j), i+j=k
  3. 구간만 시퀀셜 merge

2 Sort Ch13

방법특징
Merge sort병합 기반 · 위 Ch12 활용
Radix sortbit별 scan + scatter · O(N·k)
Bitonic네트워크형 · 고정 크기

실무: cub::DeviceRadixSort · thrust::sort

3 Sparse Matrix Ch14

format저장장단
COO(row,col,val)단순·랜덤 어려움
CSRrow_ptr, col_idx, val표준 · SpMV 좋음
ELL고정 폭 padded균일 row에 유리
JDS / Hybrid정렬·혼합load balance ↑
y = A·x (CSR)
for r: y[r] = Σk=row_ptr[r]..row_ptr[r+1] val[k]·x[col_idx[k]] 한 row → 한 warp가 담당 시 load balance 문제

4 CSR 생성 = scan!

  • row별 nnz 카운트 → exclusive scan → row_ptr
  • Ch11 Scan과 직접 연결 ★

5 최종 체크리스트 ★★ 단권화 점검

항목OK?
warp 정렬된 분기 (divergence ↓)
Coalesced global access
Shared mem으로 재사용
Bank conflict 회피 (padding)
occupancy 50%+ · 레지스터 과다 X
atomic은 smem에서 먼저
boundary if 빠짐없음
__syncthreads uniform한 위치
kernel err / memcheck 통과
GPU 튜닝 공통 5: 병코재분레
메모리 6종: 전상지공레텍
CUDA 5단계: 할복실복해
Compute 5: SWDLT

6 패턴 요약

Conv Stencil Hist Reduce Scan Merge Sort SpMV

공통 변환: tile + shared + coalesce + atomic 최소화 + scan 기반

실무: CUB·thrust·cuBLAS·cuSPARSE 먼저 고려. 직접 구현은 특수 케이스만.

1 Graph 표현 CSR

정의 희소 그래프는 CSR로: row_ptr[V+1], col_idx[E].
vertex v의 이웃 = col_idx[row_ptr[v] .. row_ptr[v+1])
V=5, E=7
row_ptr: [0,2,3,5,6,7]
col_idx: [1,2, 3, 0,4, 4, 2]

Ch14의 SpMV와 동일 구조 → Scan으로 빌드 (Ch11 재활용)

2 BFS = level-sync ★

  • source에서 level 단위 동심원으로 확장
  • 각 level = 1 kernel launch
  • depth = O(diameter) ≪ V
frontierL+1 = {u | ∃v∈frontierL, (v,u)∈E, level[u]=∞} level[u]=L+1로 세팅

3 Vertex-centric (naive)

__global__ void bfsV(int* rp,int* ci,int* lvl,int L,int* done){
  int v=blockIdx.x*blockDim.x+threadIdx.x;
  if(lvl[v]==L){
    for(int k=rp[v]; k<rp[v+1]; ++k){
      int u=ci[k];
      if(lvl[u]==INF){ lvl[u]=L+1; *done=0; }
    }
  }
}
degree 편차 큼 → load imbalance. 고차 vertex가 warp 하나 독점.

4 Edge-centric 개선

  • edge 단위로 thread 배정 → degree 편차 무관
  • frontier에 속한 source의 edge만 필터링 (scan)
  • work-efficient ↑

5 Frontier-based ★ 프론

  1. 현재 frontier 큐 유지 (크기 ≪ V)
  2. thread당 frontier 원소 → 이웃 탐색
  3. 새 vertex는 atomic으로 next frontier에 push
  4. frontier swap · level++
work = O(V+E), 실질 access = frontier 합 모든 vertex 검사하는 naive보다 훨씬 빠름

6 Direction-Optimizing (Beamer) ★

정의 frontier가 크면 bottom-up(모든 미방문 vertex가 부모 있나 확인), 작으면 top-down(기존 push).
  • social graph 등 small-world에서 대폭 속도 ↑
  • heuristic: |frontier| / V 임계값
Privatization: next frontier도 block별 smem에 먼저, 후 global atomic 한번 — Ch9 hist와 동일 패턴.
BFS 4키: 레프엣방 (벨 sync · 론티어 · 지 중심 · 향 전환)

1 학습 파이프라인

  1. Forward: x → (conv/FC/ReLU)… → ŷ
  2. Loss: L(ŷ, y)
  3. Backward: ∂L/∂w (chain rule)
  4. Update: w ← w − η·∂L/∂w (SGD/Adam)

mini-batch = data-parallel 병렬 축 ★

2 FC Layer = GEMM

Y = X·W + b
X: [B, K], W: [K, M], Y: [B, M] B=batch, K=in, M=out  → Ch5 tiled matmul 그대로
  • cuBLAS gemm 호출이 실무 표준
  • Tensor Core FP16/BF16 input · FP32 accumulate

3 Convolution Layer

Y[n,m,r,c] = ΣΣΣ X[n,k,r+i,c+j]·W[m,k,i,j] + b[m] n=batch, m=out ch, k=in ch, (i,j)=필터 위치

5중 loop → naive 커널은 CGMA 낮음. 재구성 필요.

4 im2col + GEMM ★ 펴서 곱하기

Conv = (unfold X) × (reshape W)
X_col: [K·Kh·Kw, N·Oh·Ow]
W_row: [M, K·Kh·Kw]
Y    : W_row · X_col   → GEMM!
  • X_col은 중복 저장 → memory ↑ but GEMM 재사용 큰 이득
  • cuDNN이 내부에서 선택: direct / im2col / FFT / Winograd

5 알고리즘 선택

방법유리 조건
Direct큰 채널 + 작은 spatial
im2col+GEMM범용, 안정
Winograd3×3 필터, F(2,3)/F(4,3)
FFT큰 필터 (≥7×7)

cuDNN findAlgorithm이 자동 벤치

6 Backward

  • dW = XT·dY (GEMM)
  • dX = dY·WT (GEMM) — conv: transposed conv
  • 세 개의 GEMM 모두 같은 tile 커널로 처리

7 Mixed Precision ★

정의 FP16/BF16 저장·연산 + FP32 accumulate A100 Tensor Core: FP16 312 TF (FP32의 ~16×)
  • Loss scaling: underflow 방지
  • BF16: 지수 범위 FP32와 동일 → scaling 불필요
accumulate는 FP32 유지 — 이유: 부분합이 긴 합, 오차 누적.

8 Batch 병렬 + 분산

  • Data parallel: batch 분할 → N GPU, gradient AllReduce (NCCL)
  • Model parallel: layer 분할 (모델이 한 GPU에 못 들어갈 때)
  • Pipeline parallel: stage 파이프라인 (GPipe, 1F1B)

9 라이브러리 실무

cuBLAS cuDNN CUTLASS NCCL TensorRT

직접 커널: fused activation/norm 정도. 핵심 GEMM/Conv는 벤더 라이브러리.

DL 핵심 3: 젬임믹 (GEMM · im2col · mixed precision)

1 왜 MPI + CUDA?

  • 단일 노드 GPU 메모리 한계 (80 GB) 초과
  • 대형 과학 시뮬·LLM 학습
  • 노드 간: MPI (message passing) · 노드 내: CUDA (shared)
Node0 [GPU0 GPU1 ...] ─┐
Node1 [GPU0 GPU1 ...] ─┼─ IB / NVLink Switch
...                    │
  └─ MPI_COMM_WORLD ───┘

2 MPI 핵심 API

호출의미
MPI_Init초기화
MPI_Comm_rank내 프로세스 ID
MPI_Comm_size총 프로세스 수
MPI_Send/RecvP2P (blocking)
MPI_Isend/Irecvnon-blocking + Wait
MPI_Allreduce모든 rank가 reduce 결과
MPI_Bcastrank0 → all
MPI_Barrier동기화

3 Domain Decomposition ★

정의 큰 격자를 rank 수만큼 분할, 각 rank가 자신 영역을 담당. 경계는 ghost/halo를 인접 rank와 교환.
┌──────┬──────┬──────┐
│rank0 │rank1 │rank2 │
│   ▒▒▒│▒▒▒▒▒▒│▒▒▒   │  ← halo 교환
└──────┴──────┴──────┘
  • 1D / 2D / 3D 분할 (surface/volume 비 고려)
  • Stencil(Ch8)의 노드간 확장판

4 전형 루프

for(step){
  MPI_Isend(halo_right, right_rank);  // 비동기
  MPI_Irecv(halo_left,  left_rank);
  compute_interior<<<>>>(...);        // 통신과 겹침
  MPI_Waitall(...);
  compute_boundary<<<>>>(...);
}
통신·계산 overlap이 strong scaling의 핵심. Isend/Irecv 필수.

5 CUDA-aware MPI ★

  • GPU 포인터를 MPI_Send에 직접 전달 OK
  • GPUDirect RDMA: PCIe/IB로 GPU↔NIC 직접 DMA (CPU 경유 ×)
  • OpenMPI, MVAPICH2 등 지원
effective BW ≈ min(NVLink, IB/Ethernet) NVLink ~900 GB/s · IB HDR ~25 GB/s ∴ inter-node가 병목

6 NCCL DL용 Collective

  • GPU 특화 all-reduce / broadcast / all-gather
  • ring / tree 알고리즘 자동 선택
  • DL에서 MPI_Allreduce 대체재로 선호

PyTorch DDP, Horovod 내부 엔진

7 Scaling 개념

종류고정확장
Strong총 문제 크기rank ↑ → time ↓
Weakrank당 크기rank ↑ → 크기 ↑

Amdahl (Ch1) ↔ Gustafson 대응

MPI+CUDA 4: 도할오어 (메인 분할 · 로 교환 · 버랩 · 웨어 MPI/NCCL)