6.1.11 · D4 · HinglishParallelism & Multicore

ExercisesVector - SIMD instructions (SSE, AVX, NEON)

3,422 words16 min read↑ Read in English

6.1.11 · D4 · Hardware › Parallelism & Multicore › Vector - SIMD instructions (SSE, AVX, NEON)

Yeh page ek self-testing ladder hai. Har problem ek clean question batata hai; poora worked solution ek collapsible [!recall]- callout ke andar chhupa hua hai taaki tum pehle try kar sako, phir reveal karo. Rungs L1 Recognition se shuru hote hain (kya tum iska naam bata sakte ho?) aur L5 Mastery tak jaate hain (kya tum ise design aur defend kar sakte ho?). Yahan sab kuch the parent SIMD note par build karta hai aur Loop Vectorization, Data Paralelism, aur Auto-vectorization ko touch karta hai.

Shuru karne se pehle, hum har woh symbol phir se earn karte hain jo poori page mein use hoga taaki koi pehli line se hi confused na ho.

Neeche wala figure , , aur trays ko concrete banata hai. Formula padhne se pehle ise dekho: coloured cells woh elements hain; horizontal brackets unhe slots ki trays mein group karte hain. Dhyaan do ki teesri tray sirf aadhi bhari hai (4 mein se 2 slots) — phir bhi yeh poori tray hai. Woh ek picture hi kyun is page ke har formula mein ceiling aati hai, yeh batata hai.


Level 1 — Recognition

L1.1 — Register ka naam batao

Q. Ek x86 CPU mein ek 256-bit register hai jo 8 single-precision (32-bit) floats rakhta hai. Konse SIMD instruction-set family ne ise introduce kiya, aur register ko kya kehte hain?

Recall Solution
  • floats — description se match karta hai.
  • 256-bit registers YMM registers hain, jo AVX (Advanced Vector Extensions) ne introduce kiye.
  • (128-bit XMM = SSE; 512-bit ZMM = AVX-512; 128-bit Q = ARM par NEON.)

Answer: AVX, register YMM.

L1.2 — Bit widths se lane count

Q. Ek 128-bit NEON Q register mein kitne 8-bit values fit hote hain?

Recall Solution

Answer: 16 lanes.

L1.3 — Same-op rule

Q. True ya false: ek single SIMD instruction pehle lane pair ko add kar sakti hai jabki doosre pair ko multiply kare.

Recall Solution

False. SIMD = Single Instruction, Multiple Data. Saare lanes same step mein same operation execute karte hain. Alag ops per lane ke liye alag instructions (ya masking) chahiye.


Level 2 — Application

L2.1 — Ideal speedup, large array

Q. Tum floats AVX () se process karte ho. Overhead ignore karte hue, speedup kya hai?

Recall Solution

(evenly divide hota hai). . Answer: — ideal kyunki ka clean multiple hai.

L2.2 — Remainder ke saath vector instructions ki count

Q. Tum floats SSE () se process karte ho. Kitne SSE instructions chalenge, aur kitne scalar cleanup steps bachenge?

Recall Solution
  • Full trays: SSE instructions elements cover karte hain.
  • Remainder: scalar steps. Answer: 25 SSE instructions, 0 cleanup. (100, 4 se divisible hai, isliye tail empty hai.)

L2.3 — Ragged length ke saath realistic time

Q. elements, , har step ki cost cycles. , , aur compute karo ( ko do decimals tak round karo).

Recall Solution
  • cycles.
  • Trays: . cycles.
  • . Answer: , , . Exactly 8 kyun nahi? Chauthaa tray aadha empty hai (8 mein se sirf 6 lanes use) phir bhi ek poora step cost karta hai.

Neeche wala figure exactly yahi scenario draw karta hai. Chauthi tray dekho: iske blue slots woh 6 real elements hain, grey slots woh 2 wasted lanes hain. Woh grey slots ek poora cycles consume karte hain phir bhi zero useful work karte hain — yahi visible reason hai ki hai, ideal nahi. Is page ke har ragged-length problem mein yahi picture ka variation hai.


Level 3 — Analysis

L3.1 — Speedup kahan gaya?

Q. 8 floats ka dot product karta hai 8 multiplies + 7 adds = 15 scalar ops. AVX version karta hai 1 multiply + 4 reduction ops = 5 ops. ke liye ideal speedup 8× hai, phir bhi measured speedup sirf × hai. Explain karo kyun horizontal reduction zyaadaatar ideal speedup destroy kar deta hai.

Recall Solution
  • Element-wise multiply bilkul parallel hai: 8 lanes, 1 instruction — woh part hai 8×.
  • Horizontal sum (lanes ke across add karna) data-parallel nahi hai: lane 0 ko lane 4 mein add karna hoga, phir lane 2 mein, etc. Vector hardware columns ke neeche cheaply sum karta hai lekin ek row ke across sirf steps ke tree mein.
  • ke liye: reduction pairwise stages leta hai (aur 128-bit extract bhi), matlab ~4 ops jo essentially serial overhead hai ek fast multiply ke upar sawaar.
  • Toh sasta parallel part (1 op) fixed reduction cost (4 ops) se dab jaata hai: . Answer: Cross-lane reduction logarithmic serial work hai jo ke saath shrink nahi karta; yeh speedup ko se kaafi neeche cap kar deta hai. Lesson: SIMD tab chamakta hai jab output bhi ek vector ho, na ki ek single scalar sum.

L3.2 — Aligned vs unaligned bandwidth

Q. Cache lines 64 bytes ki hain. Address 64 par shuru hone wala ek aligned 32-byte AVX load kitne cache lines touch karta hai? Address 48 par shuru hone wala ek unaligned load kitne touch karta hai? Bandwidth ratio kya hai?

Recall Solution
  • Aligned load: bytes poori tarah cache line ke andar hain → 1 cache line.
  • Unaligned load: bytes line aur line ke beech straddled hain → 2 cache lines.
  • Ratio : unaligned load double memory traffic pull karta hai. Answer: 1 line vs 2 lines; misaligned access ke liye 2× bandwidth cost.

L3.3 — Break-even array size

Q. Ek vectorized loop mein 12 cycles ka fixed setup cost hai (constants load karna, aligning). Har AVX step cycles cost karta hai aur elements karta hai. Scalar cycles per element cost karta hai bina kisi setup ke. Kis ke neeche scalar actually faster hoga?

Recall Solution
  • Scalar: .
  • SIMD: .
  • Chhote try karo. ke liye: ; → SIMD jeet jaata hai.
  • ke liye: ; → tie.
  • ke liye: ; → scalar jeet jaata hai. Answer: ke liye scalar faster hai; par tie hai; par SIMD jeet jaata hai. 12-cycle setup ko kaafi elements par amortize karna zaroori hai.

Level 4 — Synthesis

L4.1 — Ek masked branch design karo

Q. Tumhe if (a[i] > 0) b[i] = a[i]*2; else b[i] = a[i]+1; ko 8 floats ke liye AVX se vectorize karna hai, jo per-lane branching forbid karta hai. Branch-free lane sequence outline karo aur vector instructions count karo.

Recall Solution

Har lane ke liye dono results compute karo, phir comparison mask use karke blend karo:

  1. mask = cmpgt(a, 0) — 1 op (jahan ho woh lanes all-ones ban jaate hain).
  2. t1 = mul(a, 2) — "then" branch value, 1 op.
  3. t2 = add(a, 1) — "else" branch value, 1 op.
  4. b = blend(t2, t1, mask) — jahan mask set ho wahan pick karo, warna , 1 op. Total: 4 vector instructions (load/store ke alaawa). Koi branch nahi, saare lanes ek ek instruction maante hain. Kyun kaam karta hai: hum control-flow decision (jo SIMD ko pasand nahi) ko dono paths par arithmetic + ek select (jo SIMD ko bahut pasand hai) se trade karte hain. Cost: hum hamesha unused branch compute karte hain, lekin per-lane mispredicted jumps se bachte hain.

L4.2 — ke liye two-phase loop

Q. AVX () ke saath floats par ek for loop vectorize karo: SIMD iteration count, scalar-cleanup count, aur total loop iterations batao.

Recall Solution
  • SIMD iterations = full trays ki count = iterations, har ek 8 elements cover karta hai → elements handle ho jaate hain.
  • SIMD se cover hue elements = ; scalar cleanup = iterations.
  • Total loop iterations = . Answer: 125 SIMD iterations, 0 cleanup, 125 total. (1000, 8 se divisible hai, isliye tail vanish ho jaati hai.) Distinction note karo: 125 iteration count hai; 1000 woh element count hai jo ye cover karte hain — in dono ko kabhi confuse mat karo.

L4.3 — Saturating vs wrapping brightness

Q. 8-bit pixels (max value 255). Pixel values mein brightness 40 add karo using (a) wrapping add aur (b) saturating add. Dono output vectors batao.

Recall Solution
  • Wrapping (mod 256): ; ; ; . → — bright pixels dark ho jaate hain (visual garbage).
  • Saturating (255 par clamp): ; ; ; . → — bright pixels bright rehte hain. Answer: wrapping ; saturating . Image code ko hamesha saturating (vqadd) chahiye.

Level 5 — Mastery

L5.1 — ISA choose karo aur numbers se justify karo

Q. Ek grayscale filter ek image process karta hai, ek byte per pixel, har pixel par same add-and-clamp karta hai. SSE (128-bit, 16 bytes/op), AVX-512 (512-bit, 64 bytes/op), aur NEON (128-bit, 16 bytes/op) ke liye instruction counts compare karo. Kaun vector-instruction count minimize karta hai, aur SSE se kitne factor par?

Recall Solution

Total pixels bytes.

  • SSE / NEON ( bytes): instructions.
  • AVX-512 ( bytes): instructions.
  • Factor: . Answer: AVX-512 count minimize karta hai vector ops se — SSE/NEON se 4× kam. Edge cases defend karo: dono 16 aur 64 se divisible hai, isliye koi scalar tail nahi hai; image row length (1920) bhi 64 ka multiple hai, isliye har SIMD load naturally aligned hai — koi unaligned penalty nahi. Yeh ideal SIMD workload hai: uniform op, koi branches nahi, aligned, huge .

L5.2 — Mixed workload par Amdahl ceiling

Q. Ek program apna 80% time ek aise loop mein spend karta hai jise tum × par vectorize kar sakte ho; baaki 20% inherently scalar hai. Whole-program speedup kya hai? Even if ho toh absolute ceiling kya hai?

Recall Solution

Total time maano. Vectorizable part , scalar part . Ceiling jab : term ho jaata hai, leaving Answer: whole-program speedup ×; hard ceiling × (yeh Amdahl's law hai — 20% scalar part sab kuch cap kar deta hai). Lesson: vector register ko ek point ke baad widening karne se kuch nahi milta jab tak scalar 20% ko bhi attack nahi karte.

L5.3 — Ek robust kernel design + defend karo

Q. Words mein, c[i] = a[i]*b[i] ko arbitrary ke liye AVX () se vectorize karne ka ek complete robust plan likho, , , aur misaligned pointers ke liye correctness guarantee karte hue.

Recall Solution

Ek correct-for-all-cases plan, chaar parts mein:

  1. handle karo: simd_count = (n/8)*8 compute karo aur main loop guard i < simd_count ke saath chalao, phir tail guard i < n ke saath. ke liye dono simd_count=0 aur n=0 hain, isliye dono loops zero iterations chalate hain — koi special-case code nahi chahiye, lekin confirm karo ki guards zero passes produce karte hain (karte hain).
  2. Alignment: agar pointers misaligned ho sakti hain, unaligned load use karo (_mm_loadu_ps / vmovups) — yeh kisi bhi address ke liye safe hai aur sirf ~2× worst-case bandwidth of L3.2 pay karta hai jab load cache line straddle kare. Aligned form (vmovaps) kabhi mat use karo aise pointer par jo tumne khud align nahi kiya: yeh misaligned address par fault karta hai.
  3. Main SIMD phase: simd_count = (n/8)*8; loop for (i = 0; i < simd_count; i += 8) jo har tray ke liye load-a, load-b, vmulps, store-c karta hai. Yeh fast path hai jo ideal carry karta hai.
  4. Scalar tail: for (i = simd_count; i < n; i++) c[i] = a[i]*b[i]; — yeh har leftover element handle karta hai, isliye total hamesha correct hai chahe 8 se divide ho ya nahi.

Saare required edge cases trace karo:

  • : simd_count = (0/8)*8 = 0 → SIMD loop skip, tail loop skip → 0 work, correct.
  • : simd_count = (5/8)*8 = 0 → SIMD skip, tail saare 5 scalar multiplies karta hai → correct.
  • : simd_count = (100/8)*8 = 96 → 12 SIMD trays 96 elements cover karte hain, tail aakhri 4 karta hai → correct.
  • Misaligned pointer: vmovups kabhi fault nahi karta, isliye upar ke charon cases small bandwidth cost par bhi hold karte hain.

Answer: unaligned loads + ek simd_count-bounded main loop + ek scalar cleanup loop = har ke liye (0 aur chhoti values samait) aur har alignment ke liye correct, thoda slower unaligned load ke liye guaranteed safety trade karta hai. Pehle correctness; aligned loads par sirf tabhi tighten karo jab tum khud allocation ke owner ho aur address guarantee kar sako.


[!recall]- Har level ke liye one-line self-check

L1 — lanes = register bits ÷ element bits
haan; 256/32 = 8
L2 — trays ke liye ceiling use karo, plain division nahi
trays
L3 — real setup, reduction, alignment se cap hota hai
dot product ke liye
L4 — hamesha scalar tail add karo; saturating int ops use karo
warna elements skip ho jaate hain / wrap to black
L5 — Amdahl whole-program speedup cap karta hai
, ceiling 5

Related paths: Loop Vectorization · Auto-vectorization · Data Paralelism · Instruction-Level Parallelism · Cache Optimization · Flynn's Taxonomy · GPU Architecture.