| 축 | CPU (latency) | GPU (throughput) |
|---|---|---|
| 목표 | 단일 thread 지연 최소 | 총 FLOPs 최대 |
| ALU | 소수 · 대형 · 저지연 | 다수 · 소형 · 장지연 허용 |
| 캐시 | L1/L2/L3 대용량 | 작음 · BW 버퍼 |
| 제어 | branch predict · OoO | in-order · SIMT mask |
| 메모리 | 강한 일관성 | relaxed · ~10× BW |
| 쓰레드 | 수~수십 | 수만 |
| 지표 | A100 SXM4 | 24-core CPU |
|---|---|---|
| FP64 peak | 9.7 TF | ~0.33 TF |
| FP32 peak | 19.5 TF | ~0.66 TF |
| TF32 TC | 156 TF | — |
| FP16 TC | 312 TF | — |
| BW | 2.0 TB/s | ~0.2 TB/s |
| Thread | ~221K (108 SM × 2048) | 48 HT |
Source: NVIDIA A100 Datasheet v1.3 · 출처 고정값만 표기 · 실측 아님
| 모델 | 범위 | 특징 |
|---|---|---|
| CUDA | single node | explicit control · C/C++ ext |
| OpenCL | cross-vendor | CUDA와 유사 · JIT |
| OpenMP | shared-mem | pragma · GPU offload |
| MPI | cluster | message passing ↗ §16 |
cudaMalloc(&d_A, n*sizeof(float))cudaMemcpy(d_A, h_A, n, H2D)kernel<<<G, B>>>(d_A, ...)cudaMemcpy(h_A, d_A, n, D2H)cudaFree(d_A)| 한정자 | 호출측 | 실행측 | 비고 |
|---|---|---|---|
__host__ | host | host | 기본값 |
__global__ | host (+dyn) | device | 반드시 void |
__device__ | device | device | inline 기본 |
__host__ __device__ | 양쪽 | 양쪽 | 공통 유틸 |
__global__ void vecAdd(const float* A, const 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; int G = (n + T - 1) / T; // ceil div vecAdd<<<G, T>>>(d_A, d_B, d_C, n);
if (i < n) boundary check 필수. n이 T 배수 아닐 때 OOB 발생.
__syncthreads(), shared memory OKgrid[ block(0,0) block(1,0) ... ]
└─ block[ t(0,0) t(1,0) ... t(31,0) ← warp 0
t(0,1) ... t(31,1) ← warp 1 ]
| 변수 | 의미 | 최대 |
|---|---|---|
gridDim | grid 내 block 수 | 2³¹-1, 65535, 65535 |
blockDim | block 내 thread 수 | 1024, 1024, 64 (곱 ≤ 1024) |
blockIdx | block의 grid 좌표 | — |
threadIdx | thread의 block 좌표 | — |
#define CUDA_CHECK(x) do{ \ cudaError_t e = (x); \ if (e != cudaSuccess){ \ fprintf(stderr, "CUDA %s:%d: %s\n", \ __FILE__,__LINE__, cudaGetErrorString(e)); \ exit(1); \ } \ } while(0) CUDA_CHECK(cudaMalloc(&d, n)); kernel<<<G,B>>>(d); CUDA_CHECK(cudaGetLastError()); // launch CUDA_CHECK(cudaDeviceSynchronize()); // async
kernel 호출 자체는 async — sync 후에야 kernel 내부 오류 확정.
foo.cu
└─ nvcc frontend
├─ host C++ → g++/clang → host object
└─ device code
└─ cicc → NVVM IR → libNVVM → PTX (.ptx)
└─ ptxas → SASS (cubin, sm_80/sm_90)
└─ fatbin → host object 링크
-arch=sm_80 Ampere · -arch=sm_90 Hopper| API | 용도 |
|---|---|
cudaMallocHost | pinned host · H↔D BW ↑ |
cudaMallocManaged | Unified Memory · page fault migrate |
cudaMemcpyAsync | stream 기반 async copy |
cudaMemPrefetchAsync | UM page를 GPU로 pull |
cudaMemset | device 메모리 초기화 |
cudaGetLastError 누락).
threadIdx·blockIdx 모두 (x, y, z) → 자연스런 mappingdim3 block(16, 16); // 256 thread dim3 grid((W+15)/16, (H+15)/16); // ceil kernel<<<grid, block>>>(...);
for(i; i<N; i+=stride) — fixed grid로 큰 N 처리
for(int i = blockIdx.x*blockDim.x + threadIdx.x; i < N; i += gridDim.x*blockDim.x){ out[i] = f(in[i]); }
.x = col (너비축), .y = row (높이축). 반대로 쓰면 coalescing 붕괴.
dim3 blk(8, 8, 8); // 512 thread dim3 grd((W+7)/8, (H+7)/8, (D+7)/8);
MRI · CT · fluid sim · 3D stencil에 쓰임.
__global__ void rgb2gray(uchar* out, const 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; const uchar* p = &in[3*i]; out[i] = 0.21f*p[0] + 0.72f*p[1] + 0.07f*p[2]; } }
__global__ void matMul(float* C, const float* A, const 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.0f; for (int k = 0; k < N; ++k) s += A[r*N + k] * B[k*N + c]; C[r*N + c] = s; } }
__launch_bounds__(maxT, minB)로 컴파일러 힌트if (r<H && c<W) return;Grid
└─ (Hopper+) Cluster ← cluster.sync · DSMEM
└─ Block (CTA) ← __syncthreads
└─ Warp = 32 thread ← __syncwarp · shfl
└─ Thread ← register · PC
SM (108개) ├─ 4 processing block (partition) │ ├─ warp scheduler × 1 │ ├─ INT32 unit × 16 │ ├─ FP32 unit × 16 │ ├─ FP64 unit × 8 │ ├─ Tensor Core (3rd gen) × 1 │ ├─ LD/ST unit × 8 │ └─ SFU × 4 (sin/exp/rsqrt) ├─ Register File (64K × 32b = 256KB) ├─ L1 / Smem 통합 (192 KB, carveable) ├─ L1 instruction cache └─ Constant cache (read-only)
Source: NVIDIA A100 GPU Architecture Whitepaper v1.0. Hopper 내부 상세 ↗ V02 §1.
| GPU | SM | FP32 core | TC |
|---|---|---|---|
| V100 (sm_70) | 80 | 5120 | 640 (1st) |
| A100 (sm_80) | 108 | 6912 | 432 (3rd) |
| H100 (sm_90) | 132 | 16896 | 528 (4th) |
__syncwarp() 필수| 항목 | SIMD | SIMT |
|---|---|---|
| 레인 | 고정 vector width | thread ID로 주소·분기 |
| 분기 | 명시적 mask 코드 | HW 자동 mask / serialize |
| 메모리 | aligned gather | lane별 독립 주소 |
| 모델 | data-parallel | SPMD |
| 예 | AVX-512 · ARM SVE | NVIDIA warp · AMD wavefront |
| 단위 | 통신 | 동기 | 한계 |
|---|---|---|---|
| Thread | register | — | 255 reg |
| Warp | shfl · vote | __syncwarp | 32 thread |
| Block | smem | __syncthreads | 1024 thread · 48+ KB smem |
| Cluster | DSMEM | cluster.sync | ≤16 block (sm_90+) |
| Grid | global | kernel 경계 | 2³¹ block |
grid_group::sync() (persistent kernel ↗ V05 §9)t = tx + ty·Bx + tz·Bx·Byif (tid % 2 == 0) // ❌ warp ½ mask even_work(); else odd_work(); // 두 경로 직렬
if (tid / 32 ...) 처럼 warp 전체가 같은 path.
y = c ? a : b → y = a*c + b*(1-c)@p instr 로 변환| API | scope | 용도 |
|---|---|---|
__syncthreads() | block | smem 가시성 |
__syncwarp(mask) | warp | ITS 후 필수 |
__threadfence() | device | global 순서 강제 |
__threadfence_block() | block | smem 순서 강제 |
cluster.sync() | cluster | sm_90+ |
__syncthreads() 를 if 안쪽 에 두면 일부 thread 만 도달 → deadlock. 조건을 uniform 하게 만든 뒤 호출.
| 자원 | A100 /SM | H100 /SM | 영향 |
|---|---|---|---|
| Blocks | 32 | 32 | block 수 상한 |
| Threads | 2048 | 2048 | warp 수 상한 |
| Regs | 65,536 | 65,536 | thread당 reg |
| Smem | 164 KB | 228 KB | block당 shared |
__launch_bounds____global__ __launch_bounds__(256, 4) // T, minBlocks/SM void k(...){ ... }
-Xptxas -v 로 실제 reg/smem 확인| 종류 | scope | latency | 크기 | 선언 |
|---|---|---|---|---|
| Register | thread | 1 cyc | 255/thr | auto |
| Shared / L1 | block | ~30 cyc | 164/228 KB | __shared__ |
| DSMEM | cluster | ~50 cyc | ≤16 SM | sm_90+ |
| Constant | device RO | ~L1 cache | 64 KB | __constant__ |
| L2 | device | ~200 cyc | 40/50 MB | auto |
| Texture | grid RO | spatial cache | — | tex API |
| Local | thread | ~DRAM | stack spill | auto |
| Global (HBM) | grid · host | ~400 cyc | 40/80 GB | __device__ |
Source: A100 / H100 Whitepaper. latency는 근사치, 세대별 실측 ↗ V02 §14.
| level | A100 BW | H100 BW |
|---|---|---|
| Reg file /SM | — | — |
| Smem /SM | ~19 TB/s | ~33 TB/s |
| L2 | ~4 TB/s | ~5.5 TB/s |
| HBM | 2.0 TB/s | 3.35 TB/s |
| NVLink | 600 GB/s | 900 GB/s |
| PCIe Gen4 x16 | 64 GB/s | — |
| PCIe Gen5 x16 | — | 128 GB/s |
| 지표 | A100 SXM4 80GB | H100 SXM5 80GB |
|---|---|---|
| Compute Cap | 8.0 | 9.0 |
| SM 수 | 108 | 132 |
| FP32 core | 6,912 | 16,896 |
| Tensor Core | 432 (3rd) | 528 (4th) |
| Register / SM | 256 KB | 256 KB |
| Smem max / SM | 164 KB | 228 KB |
| L2 cache | 40 MB | 50 MB |
| HBM | 80 GB HBM2e | 80 GB HBM3 |
| Mem BW | 2.0 TB/s | 3.35 TB/s |
| FP64 peak | 9.7 TF | 34 TF |
| FP32 peak | 19.5 TF | 67 TF |
| TF32 TC | 156 TF | 495 TF |
| FP16 / BF16 TC | 312 TF | 989 TF |
| FP8 TC | — | 1,979 TF |
| NVLink | 600 GB/s | 900 GB/s |
| TDP | 400 W | 700 W |
세대별 신기능 표 ↗ V02 §7, §8.
-Xptxas -v 로 "X bytes stack frame" 확인| CUDA C | PTX | backing |
|---|---|---|
| auto var | .reg | register file |
| spill | .local | HBM (thread) |
__shared__ | .shared | SRAM (block) |
__constant__ | .const | const cache |
__device__ | .global | HBM |
| tex / surf | .tex | read-only L1 |
| kernel arg | .param | const bank |
PTX state space 상세 ↗ V03 §3.
Roofline 상세 공식과 multi-tier 차트 ↗ V18 §1~3.
__syncthreads()__syncthreads() · 다음 phase| TILE | smem/block | CGMA | Occupancy |
|---|---|---|---|
| 8 | 0.5 KB | 1 F/B | 높음 |
| 16 | 2 KB | 2 F/B | 좋음 |
| 32 | 8 KB | 4 F/B | 감소 |
| 64 | 32 KB | 8 F/B | 낮음 |
#define TILE 32 __global__ void matMulTiled( float* C, const float* A, const float* B, int N){ __shared__ float As[TILE][TILE]; __shared__ float Bs[TILE][TILE]; int bx = blockIdx.x, by = blockIdx.y; int tx = threadIdx.x, ty = threadIdx.y; int row = by*TILE + ty; int col = bx*TILE + tx; float acc = 0.0f; // phase 순회 (K 방향) for(int ph = 0; ph < (N+TILE-1)/TILE; ++ph){ // ── 1. 협력 로드 ── int aCol = ph*TILE + tx; int bRow = ph*TILE + ty; As[ty][tx] = (row < N && aCol < N) ? A[row*N + aCol] : 0.0f; Bs[ty][tx] = (bRow < N && col < N) ? B[bRow*N + col] : 0.0f; __syncthreads(); // ── 2. 부분 내적 ── #pragma unroll for(int k = 0; k < TILE; ++k) acc += As[ty][k] * Bs[k][tx]; __syncthreads(); } if(row < N && col < N) C[row*N + col] = acc; }
iter i : load → B compute ← A iter i+1 : load → A compute ← B iter i+2 : load → B compute ← A
cp.async 로 자동화 ↗ V03 §60.0f 로드 (neutral)if(r<N && c<N)__syncthreads() 생략 금지. 다음 phase 의 로드가 아직 쓰이는 As/Bs 를 덮어써 race condition.
✓ coalesced: t0→a[0], t1→a[1], ... t31→a[31] (128 B) ✗ strided : t0→a[0], t1→a[N], ... (N trans) ✗ random : t0→a[?] (최대 32 trans)
| 접근 | row-major A[r][c] | 결과 |
|---|---|---|
| tid→c | A[r][tid] | ✓ coalesced |
| tid→r | A[tid][c] | ✗ strided |
| transpose | A[c][tid] | strided 읽기 + coalesced 쓰기 |
// AoS (Array of Struct) — ❌ strided per-field struct Atom { float x, y, z, m; }; Atom A[N]; __global__ void k(Atom* A){ int i = ...; float x = A[i].x; // stride 16B · 25% eff } // SoA (Struct of Array) — ✓ coalesced struct Atoms { float* x; float* y; float* z; float* m; }; float x = A.x[i]; // stride 4B · 100% eff
LLM tensor layout 은 자연스럽게 SoA (각 tensor 가 별도 배열).
s[tid] : bank(tid) → 모두 다른 bank ✓ OK s[2*tid] : bank 0,2,4,...,62 → 2-way conflict s[32*tid] : 모두 bank 0 → 32-way (완전 직렬) s[tid ^ pad] : XOR swizzle → conflict 회피
// ❌ 32-way on column read __shared__ float A[32][32]; // ✓ no conflict · 1 col wasted __shared__ float A[32][33];
float4) 시에는 padding 전략 달라짐float4 load = 128 B / 32 thread (1 tx)int4, double2 동일 효과__align__(16) 또는 reinterpret_cast<float4*>ld.global.v4.f32 → 4-way vectorized__global__ void conv1d(float* y, const float* x, const float* F, int N, int R){ int i = blockIdx.x*blockDim.x + threadIdx.x; if(i >= N) return; float s = 0.0f; 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; }
__constant__ 로 (broadcast cache)__constant__ float F[25]; // 5×5 filter // host cudaMemcpyToSymbol(F, h_F, sizeof(F)); // device __global__ void conv(...){ ... s += X[...] * F[k + R]; // broadcast ... }
tile: TILE=16, R=2 ┌──────── tile + halo ────────┐ │ halo ▒▒ │ inner tile │ ▒▒ │ │ R │ TILE │ R │ └─────────┴──────────────┴────┘ smem 크기 = (TILE + 2R) output 범위 = TILE input 범위 = TILE + 2R
__shared__ float sX[TILE + 2*R]; int tx = threadIdx.x; int gi = blockIdx.x*TILE + tx; // 중앙 로드 sX[tx + R] = (gi < N) ? x[gi] : 0; // 좌측 halo if(tx < R){ int li = gi - R; sX[tx] = (li >= 0) ? x[li] : 0; } // 우측 halo 유사 __syncthreads();
| mode | 규칙 | 용도 |
|---|---|---|
| zero | 경계 밖 = 0 | 기본 · 수치 안전 |
| clamp | 가장자리 복제 | edge detection |
| mirror | 반사 | 영상 |
| wrap | 순환 (periodic) | FFT · physics |
3D volume → z-axis slab 순회
slab[z] : smem (xy-plane, tile + halo)
slab[z-1, z+1] : register (z-neighbor)
→ smem 절약
┌──────┐
│ halo │ reg: u[z-1], u[z], u[z+1]
│ xy │ smem: tile(z) + halo
│ halo │
└──────┘
__global__ void histNaive( int* h, const uchar* x, int N){ int i = blockIdx.x*blockDim.x + threadIdx.x; if(i < N) atomicAdd(&h[x[i]], 1); }
__syncthreads()__global__ void histP(int* h, const uchar* x, int N, int B){ __shared__ int s[256]; for(int t = threadIdx.x; t < B; t += blockDim.x) s[t] = 0; __syncthreads(); // 각 thread grid-stride 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]); }
int curBin = -1, count = 0; for(i = ...; i < N; i += stride){ int b = x[i]; if(b == curBin) ++count; else{ if(curBin >= 0) atomicAdd(&s[curBin], count); curBin = b; count = 1; } }
| API | 용도 |
|---|---|
atomicAdd | + (int/float/double) |
atomicMin/Max | 최소·최대 |
atomicAnd/Or/Xor | 비트 연산 |
atomicExch | swap |
atomicCAS | compare-and-swap |
PTX scope qualifier (.cta / .gpu / .sys) ↗ V03 §11.
부동소수는 결합법칙 근사적 성립 — 순서 다르면 미세 차이. ↗ V09 §7
// ❌ warp 내 절반씩 mask → divergence ↑ __shared__ float s[BS]; s[tid] = input[i]; __syncthreads(); for(int st = 1; st < blockDim.x; st *= 2){ if(tid % (2*st) == 0) s[tid] += s[tid+st]; __syncthreads(); }
% 조건 → warp 절반씩 꺼짐. 모든 warp 가 divergent 상태.
// 연속 thread가 동작 — divergence ↓ for(int st = 1; st < blockDim.x; st *= 2){ int idx = 2*st*tid; if(idx < blockDim.x) s[idx] += s[idx+st]; __syncthreads(); }
// ✓ divergence 0 + bank conflict 0 for(int st = blockDim.x/2; st > 0; st >>= 1){ if(tid < st) s[tid] += s[tid+st]; __syncthreads(); }
// load 단계에서 2개 합산 int i = blockIdx.x*(blockDim.x*2) + threadIdx.x; s[tid] = input[i] + input[i + blockDim.x]; __syncthreads(); // 이후 sequential reduction
// warp (32 thread) 는 implicit sync if(tid < 32){ volatile float* vs = s; vs[tid] += vs[tid+32]; vs[tid] += vs[tid+16]; vs[tid] += vs[tid+8]; vs[tid] += vs[tid+4]; vs[tid] += vs[tid+2]; vs[tid] += vs[tid+1]; }
volatile 만으로 안전하지 않음 — __syncwarp() 또는 shuffle 사용.
// smem 불필요 · 레지스터만 float v = s[tid]; if(tid < 32){ v += __shfl_down_sync(0xffffffff, v, 16); v += __shfl_down_sync(0xffffffff, v, 8); v += __shfl_down_sync(0xffffffff, v, 4); v += __shfl_down_sync(0xffffffff, v, 2); v += __shfl_down_sync(0xffffffff, v, 1); if(tid == 0) out[blockIdx.x] = v; }
__shfl_down_sync(mask, v, offset)atomicAdd(&out[0], blockSum)cub::DeviceReduce / thrust::reducecg::reduce(tile, val, plus<>())
grid 전체 cooperative kernel ↗ V05 §2
| Stage | Div | Bank | Idle |
|---|---|---|---|
| 1 Naive | 대 | — | — |
| 2 Interleave | ↓ | 대 | — |
| 3 Sequential | 0 | 0 | ½ |
| 4 +LoadAdd | 0 | 0 | ½ |
| 5 Unroll last warp | 0 | 0 | ↓ |
| 6 Shuffle | 0 | n/a | ↓ |
| 7 Multi-blk/CUB | 0 | n/a | 0 |
__shared__ float s[BS]; s[tid] = x[i]; __syncthreads(); for(int o = 1; o < BS; o *= 2){ float v = (tid >= o) ? s[tid - o] : 0; __syncthreads(); if(tid >= o) s[tid] += v; __syncthreads(); }
for(int st = 1; st < BS; st *= 2){ int idx = (tid + 1) * 2 * st - 1; if(idx < BS) s[idx] += s[idx - st]; __syncthreads(); } // 맨 위 원소 초기화 (exclusive 의 경우) if(tid == 0) s[BS-1] = 0;
for(int st = BS/2; st > 0; st /= 2){ __syncthreads(); int idx = (tid + 1) * 2 * st - 1; if(idx < BS){ float t = s[idx - st]; s[idx - st] = s[idx]; s[idx] += t; } }
┌ blk0 ┐┌ blk1 ┐┌ blk2 ┐ local local local Σ0 Σ1 Σ2 └────── scan aux ─────┘ +0 +Σ0 +Σ0+Σ1
| 알고리즘 | work | depth | 특징 |
|---|---|---|---|
| Sequential | N | N | 기준 |
| Hillis-Steele | N log N | log N | 단순 · 짧은 N |
| Kogge-Stone | N log N | log N | work ↑ · 병렬 ↑ |
| Brent-Kung | 2N | 2 log N | work-efficient ★ |
| Sklansky | (N/2)·log N | log N | divide & conquer |
고급 변종 (segmented · warp-level · CUB) ↗ V05 §4.
// inclusive scan within warp float v = x; for(int o = 1; o < 32; o *= 2){ float t = __shfl_up_sync( 0xffffffff, v, o); if(lane >= o) v += t; }
int coRank(int k, const T* A, int m, const T* B, int n){ int iLow = max(0, k - n); int iHigh = min(k, m); while(1){ int i = (iLow + iHigh)/2; int j = k - i; if(i > 0 && j < n && A[i-1] > B[j]) iHigh = i - 1; else if(j > 0 && i < m && B[j-1] > A[i]) iLow = i + 1; else return i; } }
O(log(m+n)) per thread · total O((m+n) log(m+n)) work.
A → │A[0] A[1] A[2] A[3] │ ╱╱╱ B↓ ╱╱╱ B[0] → 대각선 = merge path B[1] B[2]
| 방법 | 복잡도 | 특징 |
|---|---|---|
| Merge sort | O(N log N) | 병합 기반 (§1-3) |
| Radix sort | O(N·k) | bit scan + scatter |
| Bitonic | O(N log²N) | 정렬 네트워크 · 고정 size |
| Sample sort | O(N log N) | pivot 기반 · 분산 |
실무: cub::DeviceRadixSort · thrust::sort. 직접 구현은 특수 요건일 때만.
// 1 pass of b-bit radix int key = in[i]; int digit = (key >> shift) & MASK; // block-level hist + scan atomicAdd(&hist[digit], 1); __syncthreads(); blockExclusiveScan(hist); // scatter int dst = hist[digit] + blockOffset(digit); out[dst] = key;
bitonic network log²N step stage 1: compare (i, i±1) stage 2: compare (i, i±2) ... stage log N: compare (i, i±N/2) 각 stage = log N substep
| 라이브러리 | 특징 |
|---|---|
cub::DeviceRadixSort | keys / key-value · stable |
cub::BlockRadixSort | block 내 radix |
thrust::sort | STL-style · radix backend |
cub::DeviceMergeSort | merge 기반 · custom compare |
| format | 구성 | 장단 |
|---|---|---|
| COO | (row, col, val) | 단순 · 랜덤 access × |
| CSR | row_ptr, col_idx, val | 표준 · SpMV · row-major |
| CSC | col_ptr, row_idx, val | col access 유리 |
| ELL | 고정 폭 padded | 균일 row · waste |
| JDS | 정렬 + jagged | load balance ↑ |
| HYB | ELL + COO | 혼합 sparsity |
A = [1 0 2 0] rp=[0,2,3,5,6]
[0 0 3 0] ci=[0,2,2,1,3,0]
[0 4 5 0] val=[1,2,3,4,5,6]
[6 0 0 0]
V=4, nnz=6
row r 의 원소 = ci, val [rp[r]..rp[r+1])
__global__ void spmv_csr( float* y, const int* rp, const int* ci, const float* val, const float* x, int V){ int r = blockIdx.x*blockDim.x + threadIdx.x; if(r >= V) return; float s = 0; for(int k = rp[r]; k < rp[r+1]; ++k) s += val[k] * x[ci[k]]; y[r] = s; }
vector · CSR-stream · merge-based kernel 전략 ↗ V05 §13.
row_ptr[v] = v 의 이웃 범위.
V=5, E=7 (directed) row_ptr: [0, 2, 3, 5, 6, 7] col_idx: [1,2, 3, 0,4, 4, 2] (v=0 neighbors: 1,2 · v=1: 3 · v=2: 0,4 · ...)
__global__ void bfsV( const int* rp, const 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; } } } }
mini-batch = data-parallel 병렬 축 · distributed 학습은 ↗ V17
5-nested loop → naive kernel CGMA 낮음 → 재구성 필요.
Conv = (unfold X) × (reshape W) X_col : [C·Kh·Kw, N·Oh·Ow] ← patch 중복 저장 W_row : [M, C·Kh·Kw] Y : W_row · X_col ← GEMM!
| 방법 | 유리 조건 |
|---|---|
| Direct | 큰 C · 작은 spatial |
| im2col+GEMM | 범용 · 안정 |
| Implicit GEMM | memory ↓ · indexing 복잡 |
| Winograd | 3×3 · F(2,3), F(4,3) |
| FFT | 큰 필터 (≥7×7) |
cuDNN findAlgorithm 자동 벤치.
Node0 [GPU0 GPU1 ...] ─┐ Node1 [GPU0 GPU1 ...] ─┼─ IB / NVLink Switch ... │ └─ MPI_COMM_WORLD ───┘
int n; cudaGetDeviceCount(&n); // 노드 내 GPU 수 cudaSetDevice(rank % n); // rank → GPU 매핑 cudaDeviceProp p; cudaGetDeviceProperties(&p, id); // p.name, p.totalGlobalMem, p.multiProcessorCount ...
int canAccess; cudaDeviceCanAccessPeer(&canAccess, src, dst); if(canAccess){ cudaSetDevice(src); cudaDeviceEnablePeerAccess(dst, 0); // 이후 dst 포인터 직접 read/write OK } cudaMemcpyPeer(d_dst, dst, d_src, src, n);
NVLink 통한 P2P DMA · PCIe 도 가능 (BW 낮음).
| API | 의미 |
|---|---|
MPI_Init | 초기화 |
MPI_Comm_rank | 내 rank |
MPI_Comm_size | 총 rank 수 |
MPI_Send/Recv | P2P blocking |
MPI_Isend/Irecv | non-blocking + Wait |
| MPI_Allreduce | 모든 rank reduce 결과 |
MPI_Bcast | rank 0 → all |
MPI_Barrier | 동기화 |
MPI_Reduce_scatter | reduce + scatter |
┌──────┬──────┬──────┐ │rank0 │rank1 │rank2 │ │ ▒▒▒ │▒▒▒▒▒▒│ ▒▒▒ │ ← halo 교환 └──────┴──────┴──────┘ 1D · 2D · 3D 분할 (surface/volume ratio 고려)
for(step){ MPI_Isend(halo_right, right_rank, ..., &req[0]); MPI_Irecv(halo_left, left_rank, ..., &req[1]); compute_interior<<<>>>(...); // overlap MPI_Waitall(2, req, status); compute_boundary<<<>>>(...); }
MPI_Send 에 직접 전달 가능ompi_info --all | grep mca_btl_openib_cuda| 항목 | MPI | NCCL |
|---|---|---|
| 범위 | 일반 HPC | DL 특화 |
| 원소 | 임의 datatype | GPU tensor |
| topology | 수동/자동 | 자동 detect |
| API | 많음 (수십) | 좁음 (collective 중심) |
| 사용처 | 과학 시뮬 | PyTorch DDP · FSDP |
collective · topology · ring/tree 상세 ↗ V15.
| 종류 | 고정 | 확장 |
|---|---|---|
| Strong | 총 문제 크기 | rank ↑ → time ↓ (Amdahl) |
| Weak | rank당 크기 | rank ↑ → 문제 ↑ (Gustafson) |
| 패턴 | 핵심 1-line |
|---|---|
| Map | thread 1개 → output 1개 · embarrassingly parallel |
| Stencil | 이웃 참조 · halo + smem tile |
| Reduction | tree · shuffle · multi-block |
| Scan | Brent-Kung · up/down-sweep |
| Histogram | privatization · atomic merge |
| Convolution | const filter + tiled halo |
| Sort/Merge | co-rank · radix scan/scatter |
| scope | 특징 |
|---|---|
| Register | 1 cyc · thread · 255 상한 |
| Shared | 30 cyc · block · 32 bank |
| L2 | 200 cyc · device · 40/50 MB |
| Global | 400 cyc · grid · 80 GB |
| Constant | broadcast · 64 KB |
| Texture | spatial cache · RO |
cudaMemcpy 방향 플래그 혼동cudaGetLastError)if 빠짐 → OOB__syncthreads() 를 분기 안쪽sync 누락)공통 변환: tile + smem + coalesce + atomic 최소화 + scan 기반.
V01 (PMPP)
│
├─► V02 (HW) ─► V03 (PTX) ─► V04 (Hopper)
│ │
├─► V05 (Primitive) │
│ │ │
│ └─► V06 (GEMM) ─► V07 (Attn)
│ │
├─► V09 (Numeric) ─► V10 (Quant)
│ │
└─► V15 (Distrib) │
├─► V16 (Serving) │
└─► V17 (Training) ◄──────┘
V11–V14: Triton · MLIR · Inductor · XLA/TVM
V18: Profiling (모든 권 뒤)
| 점검 항목 | OK |
|---|---|
| warp 정렬 분기 (div ↓) | □ |
| Coalesced global access | □ |
| Shared mem reuse (tile) | □ |
| Bank conflict 회피 (pad/swizzle) | □ |
| Occupancy 50%+ · reg 과다 × | □ |
| atomic → smem 먼저 | □ |
| boundary if 빠짐 없음 | □ |
__syncthreads uniform 위치 | □ |
| CUDA_CHECK · memcheck 통과 | □ |
CUB · thrust · cuBLAS · cuSPARSE · cuDNN · CUTLASS 먼저 고려. 직접 구현은 특수 케이스 · fused activation/norm 수준만.