Worked examples — CUDA programming model basics
6.2.13 · D3· Hardware › GPU Architecture › CUDA programming model basics
Yeh page CUDA programming model basics ka hands-on companion hai. Parent note ne machinery build ki thi: blocks SMs pe map hote hain, threads ek global index carry karte hain, aur memory ke speed tiers hote hain. Yahaan hum us machinery ko use karte hain — un har awkward cases pe jo ek real problem throw kar sakti hai.
Kuch bhi shuru karne se pehle, chalo us ek formula ko dobara anchor karte hain jis par hum poori page rely karenge. Ek lambi line of numbered lockers imagine karo — woh hai tumhari array. Hum us line ko equal blocks (fixed-size chunks) mein kaatте hain. Har block ke andar, threads se upar number hote hain.

Pehle figure padho. Horizontal strip tumhari array hai; har chhota box ek thread ka element hai, aur har box ke andar ka number us thread ka globalIdx hai. Cyan boxes ordinary threads hain; single amber box (sabse right wala, index ) woh thread hai jise hum Example 1 mein track karte hain. Boxes ke upar, white brackets boxes ko blocks mein group karte hain — teen brackets jinka label hai block 0, block 1, block 2. Har box ke neeche chhote white t0 … t3 tags hain jo us block ke andar ki seat number (threadIdx.x) dikhate hain. Pattern notice karo: seats har nayi bracket pe se restart hoti hain, jabki bada number climb karta rehta hai — woh climb hi global index hai.
Upar har symbol ka ek picture hai. Neeche do aur pieces of notation baar baar aayenge, isliye hum unhe abhi define karte hain, pehli use se pehle:
Ab matrix.
The scenario matrix
Har CUDA-indexing problem jo tumhe milegi woh in cells mein se ek hai. Neeche wale examples us cell ke label ke saath hain jo woh cover karta hai, aur saath mein woh sab mila ke sab cover kar lete hain.
| Cell | Kya cheez isse tricky banati hai | Covered by |
|---|---|---|
| A. Clean divide | block size ka exact multiple hai — koi leftovers nahi | Ex 1 |
| B. Ragged tail | divisible nahi — extra idle threads aate hain | Ex 2 |
| C. Degenerate: tiny | ek block se chhota, ya , ya | Ex 3 |
| D. Reverse map | Ek globalIdx diya hai, pata karo kaun sa block & seat tha |
Ex 4 |
| E. 2-D grid | Image / matrix — do index axes, row-major flattening | Ex 5 |
| F. Memory-cost word problem | Kya data registers, shared, ya global mein rehna chahiye? | Ex 6 |
| G. Launch-config sizing | Block size choose karo; idle threads aur wasted warp lanes count karo | Ex 7 |
| H. Exam twist: stride loop | Threads se zyada elements — ek thread kaafi handle karta hai | Ex 8 |
| I. 3-D grid | Volumes / batches — teen index axes, depth·height·width flatten | Ex 9 |
Cell A — the clean divide
Forecast: aage padhne se pehle index guess karo. (Hint: last block, last seat — bilkul last element hona chahiye.)
- Do pieces padho.
blockIdx.x = 2,blockDim.x = 4,threadIdx.x = 3. Yeh step kyun? Formula ko exactly yahi teen numbers chahiye; pehle inhe gather karo taaki baad mein kuch guess na karna pade. - Mere block se pehle wale threads: .
Yeh step kyun? Mere aage seats ke do poore chunks hain; mujhe saare skip karne honge. (Figure mein, yeh do full brackets
block 0aurblock 1hain.) - Meri seat add karo: . Yeh step kyun? Mere chunk ke andar seat mujhe mere chunk ke front door se aage le jaati hai — figure mein woh amber box hai.
- Boundary check
if (idx < N): kya hai? Haan. Yeh step kyun? Clean divide mein bhi hum test karte hain — acha habit hai, aur baad ke ragged cases ke liye protect karta hai.
Answer: globalIdx , valid.
Verify: Total threads , exactly . Jo indices produce hue woh hain — har element ek baar hit, koi dobara nahi, koi skip nahi. sabse bada hai, isliye last thread last element hit karta hai. Consistent.
Cell B — the ragged tail
Forecast: kya evenly aayega? Nahi — toh expect karo leftover idle threads. Guess karo kitne.
- Ceiling division. Hume saare elements ke liye kam se kam enough threads chahiye: Yeh step kyun? blocks threads dete hain; blocks sirf dete hain, jo elements untouched chhod deta. Hume upar round karna hai — yahi exactly ceiling bracket (upar define kiya) karta hai.
- trick kyun?
threadsPerBlock − 1add karna aur phir integer-divide karna, floating-point math ke bina round-up force karta hai. Yeh step kyun? Integer division truncate karta hai neeche; yeh offset kisi bhi non-zero remainder ko next boundary ke upar push kar deta hai. Yeh sawaal ka jawaab deta hai "kya kuch last full block ke past spill hua?" - Total threads launched: . Yeh step kyun? Blocks fixed sizes mein aate hain — tum partial block launch nahi kar sakte, isliye tum hamesha over-provision karte ho.
- Idle threads: . Inke
idxhain . Yeh step kyun? Exactly isliyeif (idx < N)exist karta hai — iske bina, threads – array ke end se aage read kar lete. Parent note ke mistakes callout mein dekho.
Answer: 4 blocks, 1024 threads launched, 24 idle.
Verify: ✓ aur ✗, isliye smallest valid count hai. Idle , aur . ✓
Cell C — degenerate & edge inputs
Forecast: woh sneaky wala hai — kuch nahi ke liye kitne blocks launch karne chahiye?
- (a) : block.
Kyun? Ek bhi element ke liye ek block chahiye; tum fraction of a block launch nahi kar sakte. Threads launched , idle . Sirf thread
idx < 1pass karta hai. - (b) : blocks. Trick se: (integer division). Kyun? Kuch karna nahi → ceiling formula blocks return karta hai.
- (c) : block. Trick: . Kyun? Exactly ek full block; offset hamein falsely tak bump nahi karta kyunki . Yeh confirm karta hai ki ceiling trick exact multiples pe kabhi over-round nahi karti.
Answer: (a) 1 block, 255 idle; (b) 0 blocks (launch guard karo), 0 idle; (c) 1 block, 0 idle.
Verify: (a) ✓. (b) ✓. (c) ✓, aur trick deta hai, nahi, isliye koi phantom extra block nahi.
Cell D — the reverse map
Forecast: kaun sa block? Roughly , toh shayad block . Exact ho jaate hain.
- Block recover karo integer division se (upar define kiya hua floor bracket — round down): Yeh step kyun? Formula tha with . se divide karke remainder drop karne se seat strip ho jaati hai aur block bach jaata hai — yeh multiplication ko invert karta hai.
- Seat recover karo remainder (modulo) se: Yeh step kyun? Remainder exactly hai "block ke front door se kitna aage" — woh seat jo step 1 mein skip hui.
Answer: block 3, seat 45.
Verify: Index forward rebuild karo: ✓. Aur , ek legal seat. Dono directions agree karte hain — division aur modulo index formula ke clean inverse hain.
Cell E — the 2-D grid
Images aur matrices ko do axes chahiye. Ek photo pe ek grid imagine karo: threadIdx.x/blockIdx.x columns count karte hain (across), threadIdx.y/blockIdx.y rows count karte hain (down).

Pehle figure padho. Bada square image ka corner hai. Faint white lines isse blocks mein kaatti hain (chaar across, chaar down). Cyan-shaded square woh ek block hai blockIdx = (2, 3) pe — uske columns hain (yani se tak) aur rows ( se aage). Us block ke andar, amber square marker hamara target pixel hai: us block ke andar seat (5, 7) column , row pe land karta hai. Amber label dono chhote index sums spell out karta hai. Notice karo rows downward increase karte hain (image convention) — y-axis bilkul isi wajah se flipped hai.
Forecast: row .y numbers use karta hai, column .x numbers use karta hai. Compute karne se pehle guess karo.
- Column (x-axis): Yeh step kyun? Same 1-D logic jaisi Example 1, sirf horizontal axis pe apply ki.
- Row (y-axis): Yeh step kyun? Vertical axis same formula ki ek doosri, independent copy hai.
- 1-D mein flatten karo (row-major):
Yeh step kyun? Memory ek seedhi line hai; pixels ki ek poori row har nayi row se pehle aati hai.
row × widthmultiply karne se upar ki saari complete rows skip ho jaati hain, phir+ colcurrent row mein chalta hai. Yeh exactly wahi 1-D idea hai apne andar nested.
Answer: pixel (row 55, col 37), flat index 28197.
Verify: Bounds: ✓, ✓. Flatten range check: max legal flat index , aur ✓. Flat se col re-derive karo: ✓; row ✓.
Cell F — the memory-cost word problem
Forecast: 8 baar door wali global memory se re-read karna wasteful lagta hai. Cycle counts guess karo.
- All-global cost: cycles. Yeh step kyun? Agar kuch bhi cached nahi hai, toh 8 reads mein se har ek full off-chip trip pay karta hai. Memory Hierarchy dekho yeh jaanne ke liye ki global ~ cycles kyun hai (off-chip, ~cm of wire + controller).
- Shared-memory cost: ek global load , phir shared reads . Total cycles. Yeh step kyun? Data on-chip stage karne ke liye expensive trip ek baar pay karo, phir saste mein re-read karo. Shared memory SM ke andar rehti hai.
- Speedup: . Yeh step kyun? Yahi ratio woh reason hai jiske liye shared-memory tiling exist karti hai — reuse ek expensive load ko amortise karta hai.
Answer: 2400 vs 340 cycles → shared memory ke saath ≈7.06× faster.
Verify: Break-even reuse count solve karo: . Toh koi bhi element jo do ya zyada baar read ho, woh already shared memory favour karta hai — aur hum ise baar read karte hain, comfortably break-even se aage. ✓
Cell G — launch-config sizing
Forecast: se evenly divide hota hai — tempting! Lekin kya allowed hai (max hai), aur kya yeh ka multiple hai? Guess karo hidden waste kahan hai.
- Option A — 256: . Threads . Idle (grid-level) . Yeh step kyun? threads whole warps hain — koi partial warp nahi. Wasted warp lanes per block , isliye total wasted lanes .
- Option B — 1000: , isliye yeh legal hai. exactly. Threads . Idle (grid-level) . Yeh step kyun? Exact divide ka matlab hai grid level pe zero leftover threads — koi ragged tail nahi. Ab tak perfect lagta hai.
- Option B ke liye warp-level waste quantify karo. Warps per block warps. Woh warps physically lanes occupy karte hain, lekin sirf threads real hain. Wasted lanes per block . Yeh step kyun? Hardware hamesha lanes ka poora warp schedule karta hai; har block ka last warp live threads ke saath aur dead lanes ke saath run karta hai. Woh dead lanes phir bhi ek scheduling slot consume karte hain — pure waste.
- Grid ke across total wasted lanes (Option B): wasted lanes. Yeh step kyun? Poori picture dekhne ke liye per-block waste ko block count se multiply karo. Option A total idle threads waste karta hai; Option B warp lanes waste karta hai — se zyada waste, apne "zero idle threads" headline ke peeche chhupa hua.
Answer: A: 3907 blocks, 1 000 192 threads, 192 idle, 0 wasted lanes. B: 1000 blocks, 1 000 000 threads, 0 grid-idle, lekin 24 wasted lanes/block = 24 000 total — worse choice hai.
Verify: A: aur ✓ (smallest count); → waste ✓. B: exactly ✓, ✓ (legal); warps/block , waste/block ✓, total ✓.
Cell H — the exam twist (grid-stride loop)
Kabhi kabhi tum purpose se threads se kam threads launch karte ho elements se (jaise ek specific GPU mein fit karne ke liye). Har thread phir loop karta hai, har baar total number of threads ke barabar aage jump karta hua. Woh jump hai stride.

Pehle figure padho. Strip ek -element array hai, boxes number kiye gaye hain. Har box ek chhota white t0…t7 tag carry karta hai jo dikhata hai threads mein se kaun sa us element ka owner hai — tum dekh sakte ho tags repeat hote hain kyunki har thread ke stride se aage jump karta hai. Thread 3 ke owned boxes bright amber mein drawn hain; do curved amber arrows uski journey trace karte hain. ke baad, agla jump pe land karta, jo strip se bahar hai — isliye thread 3 rok jaata hai. Yahi loop condition i < N apna kaam kar rahi hai.
Forecast: thread 3 element 3 se shuru karta hai aur 8 se jump karta hai. Compute karne se pehle uski list guess karo.
- Stride compute karo. . Yeh step kyun? Stride total thread count ke barabar hona chahiye taaki 8 threads array ko bina overlap ke tile karen — jaise 8 log "1-8, 9-16, ..." baar baar count off karte hain.
- Thread 3 march karo. Start ; phir . Ruk jaao, kyunki .
Yeh step kyun? Loop condition
i < Nhar thread ko cleanly halt karta hai jab woh end se bahar jaata hai — yeh ek singleif (idx < N)check ko ek aise loop se replace karta hai jo self-limit karta hai. - Survivors collect karo. Jo values
i < 20pass kar gayi woh hain . Toh thread 3 handle karta hai — ek thread se teen elements. Yeh step kyun? Grid-stride loop ka poora point yahi hai: threads elements cover karte hain har ek thode passes karke.
Answer: thread 3 → elements 3, 11, 19.
Verify: Coverage count: elements – (first pass, saare 8 threads), – (second pass), – (third pass, sirf threads – pahunche). Total ✓, har index exactly ek baar hit hua (do threads ek hi start share nahi karte, aur stride thread count ke barabar hai isliye passes kabhi collide nahi karte). ✓
Cell I — the 3-D grid
Volumes (medical scans, physics grids) aur batched tensors ko ek teesra axis chahiye: blockIdx.z / threadIdx.z depth count karte hain (kaun sa slice). Yeh same one-axis formula hai, ab teen baar stamp out kiya gaya, phir row-major trick ke nested version se flatten kiya gaya.
Forecast: teen independent sums, phir ek three-term flatten. Pehle guess karo — woh .z numbers use karta hai.
- x-coordinate: . Yeh step kyun? Horizontal axis, base formula ki ek copy.
- y-coordinate: . Yeh step kyun? Vertical axis, independent doosri copy.
- z-coordinate (depth): . Yeh step kyun? Depth axis, independent teesri copy — yahi naya piece hai jo 3-D add karta hai.
- Depth-major flatten karo ke saath: Yeh step kyun? poore slices skip karo, phir poori rows, phir across.
Answer: , flat index 124236.
Verify: Bounds: sab ✓. Max legal flat index , aur ✓. Unflatten: ✓; ✓; ✓.
Recall Self-test
Clean vs ragged: +threadsPerBlock-1 trick kya achieve karta hai? ::: Yeh integer division ko upar round (ceiling ) karne ke liye force karta hai taaki hum kabhi bhi kam blocks launch na karein, floating-point math ke bina.
Reverse map: globalIdx aur blockDim diye hue, blockIdx aur threadIdx kaise recover karte ho? ::: blockIdx globalIdx / blockDim (floor / integer divide), threadIdx globalIdx mod blockDim.
2-D flatten: row × width + col kyun? ::: Memory 1-D hai; width pixels ki har poori row agli se pehle hoti hai, isliye row × width complete rows skip karta hai aur + col current row mein chalta hai.
3-D flatten: extra term kya hai aur kyun? ::: — yeh 2-D formula apply karne se pehle poore 2-D slices skip karta hai.
Warp kya hai aur 32 kyun? ::: Warp ek hardware bundle hai 32 threads ka jo lockstep mein run karte hain; 32 ek fixed NVIDIA hardware width hai (ek instruction 32 lanes feed karta hai).
Shared vs global break-even: kitne reuses se shared memory jeetni hai? ::: 2 reuses se ( gives ).
256 ko 1000 threads/block pe kyun prefer karte ho, chahe 1000 evenly divide ho? ::: warp size ka multiple hai (0 wasted lanes); har block mein dead lanes chodta hai (grid ke across ).
See also: GPU Architecture Overview · Parallel Algorithm Design · OpenCL vs CUDA · Deep Learning Frameworks