9. Meer computerarchitectuur#
In het hoofdstuk Architectuur heb je de basis van de von Neumann architectuur geleerd. Je hebt gezien hoe een processor werkt, hoe geheugen is georganiseerd en wat het verschil is tussen CISC en RISC. In dit verdiepingshoofdstuk gaan we kijken naar geavanceerde architectuurconcepten die in moderne computers en gespecialiseerde hardware worden gebruikt.
9.1. SIMD en MIMD: parallelle verwerking#
Computers worden steeds sneller, niet alleen door hogere kloksnelheden, maar ook door parallellisme: meerdere berekeningen tegelijk uitvoeren. Er zijn verschillende manieren om dit te doen. De computerarchitect Michael Flynn bedacht in 1966 een classificatie die nog steeds gebruikt wordt: Flynn’s taxonomie.
9.1.1. Flynn’s taxonomie#
Flynn onderscheidt vier categorieën op basis van twee vragen:
Hoeveel instructiestromen (instruction streams) zijn er?
Hoeveel datastromen (data streams) zijn er?
Dit geeft vier combinaties:
Type |
Instructies |
Data |
Beschrijving |
|---|---|---|---|
SISD |
Single |
Single |
Één instructie op één stuk data tegelijk |
SIMD |
Single |
Multiple |
Dezelfde instructie op meerdere data tegelijk |
MISD |
Multiple |
Single |
Meerdere instructies op hetzelfde stuk data |
MIMD |
Multiple |
Multiple |
Meerdere instructies op meerdere data tegelijk |
SISD (Single Instruction, Single Data) is de basis von Neumann architectuur die je al kent. Eén processor voert één instructie uit op één stuk data.
MISD (Multiple Instruction, Single Data) komt in de praktijk bijna niet voor. Het theoretische voorbeeld is een systeem waarbij meerdere processors verschillende berekeningen doen op hetzelfde getal.
We gaan ons focussen op SIMD en MIMD, die wel veel worden gebruikt.
9.1.2. SIMD: Single Instruction, Multiple Data#
SIMD betekent dat je dezelfde instructie tegelijk uitvoert op verschillende stukjes data. Dit is ideaal voor taken waar je dezelfde bewerking moet doen op een grote hoeveelheid data.
Voorbeeld zonder SIMD:
Stel je wilt 4 getallen met 2 vermenigvuldigen:
A = [10, 20, 30, 40]
Zonder SIMD doe je dit stap voor stap:
Cyclus 1: 10 × 2 = 20
Cyclus 2: 20 × 2 = 40
Cyclus 3: 30 × 2 = 60
Cyclus 4: 40 × 2 = 80
Totaal: 4 cycli
Met SIMD:
Met SIMD kun je alle vier tegelijk doen:
Cyclus 1: [10, 20, 30, 40] × 2 = [20, 40, 60, 80]
Totaal: 1 cyclus!
Dit is 4× sneller!
graph TD
A[Instructie: Vermenigvuldig met 2] --> B[Data 1: 10]
A --> C[Data 2: 20]
A --> D[Data 3: 30]
A --> E[Data 4: 40]
B --> F[Resultaat: 20]
C --> G[Resultaat: 40]
D --> H[Resultaat: 60]
E --> I[Resultaat: 80]
Waar wordt SIMD gebruikt?
SIMD is perfect voor:
Afbeeldingsverwerking: dezelfde filter toepassen op alle pixels
Videobewerking: kleurcorrectie op alle frames
Geluidbewerking: volume aanpassen van alle samples
3D graphics: transformaties op alle vertices
Wetenschappelijke berekeningen: matrixoperaties
Machine learning: operaties op grote tensoren
SIMD in processors
Moderne processoren hebben speciale SIMD-instructies:
Intel/AMD: SSE, AVX, AVX-512
ARM: NEON
RISC-V: Vector Extension
Deze instructies werken met speciale brede registers die bijvoorbeeld 128, 256 of 512 bits groot zijn. In een 256-bit register passen bijvoorbeeld 8 getallen van 32-bit, die je dan allemaal tegelijk kunt bewerken.
Voorbeeld: AVX-instructie
Een normale instructie telt 2 getallen op:
ADD R1, R2 ; R1 = R1 + R2
Een AVX-instructie telt 8 paren getallen tegelijk op:
VADDPS YMM1, YMM2, YMM3 ; 8 floating points tegelijk optellen
Beperkingen van SIMD
SIMD werkt alleen goed als:
Je dezelfde operatie doet op veel data
De data mooi achter elkaar in het geheugen staat
Er geen afhankelijkheden tussen de data zijn
Voor taken met veel if-statements of onvoorspelbare patronen werkt SIMD minder goed.
Oefening 9.1
Leg uit waarom het toepassen van een filter op een foto (zoals zwart-wit maken) ideaal is voor SIMD.
Stel je hebt een array van 1000 getallen en je wilt ze allemaal met 3 vermenigvuldigen. Hoeveel cycli heb je nodig:
Zonder SIMD?
Met SIMD waarbij je 8 getallen tegelijk kunt verwerken?
Waarom is SIMD minder geschikt voor het sorteren van een lijst met getallen?
Zoek op welke SIMD-instructieset de processor van jouw computer of telefoon ondersteunt (SSE, AVX, AVX2, AVX-512, NEON, etc.). Zoek van die SIMD-instructieset een paar voorbeelden op.
9.1.3. MIMD: Multiple Instruction, Multiple Data#
MIMD betekent dat je verschillende instructies tegelijk uitvoert op verschillende data. Dit is wat er gebeurt bij multi-core processors en in gedistribueerde systemen.
Multi-core processors
Je laptop of PC heeft waarschijnlijk meerdere cores (4, 8, of zelfs meer). Elke core kan een ander programma of een ander deel van hetzelfde programma uitvoeren.
graph LR
subgraph Core1
I1[Instructie A] --> D1[Data X]
end
subgraph Core2
I2[Instructie B] --> D2[Data Y]
end
subgraph Core3
I3[Instructie C] --> D3[Data Z]
end
subgraph Core4
I4[Instructie D] --> D4[Data W]
end
Voorbeeld:
Core 1: Compileert code
Core 2: Speelt muziek af
Core 3: Rendert een video
Core 4: Download een bestand
Allemaal tegelijk, allemaal verschillende taken!
Shared Memory vs Distributed Memory
Bij MIMD zijn er twee hoofdmodellen:
Shared Memory (Gedeeld geheugen)
Alle cores hebben toegang tot hetzelfde werkgeheugen
Voorbeeld: een multi-core processor in je laptop
Voordeel: gemakkelijk data delen tussen cores
Nadeel: geheugen kan een bottleneck worden
Distributed Memory (Gedistribueerd geheugen)
Elke processor heeft zijn eigen geheugen
Processors communiceren via netwerk
Voorbeeld: een cluster van computers (supercomputer)
Voordeel: schaalbaar naar duizenden processors
Nadeel: communicatie tussen processors is traag
Parallellisme in software
Om gebruik te maken van MIMD moet software speciaal geschreven worden voor multi-threading of parallel processing. Een programma moet opgedeeld worden in taken die onafhankelijk uitgevoerd kunnen worden.
Voorbeeld in Python:
# Zonder parallellisme
for image in images:
process_image(image) # 1 voor 1
# Met parallellisme (MIMD)
from multiprocessing import Pool
with Pool(4) as p: # 4 cores
p.map(process_image, images) # 4 tegelijk!
Amdahl’s wet
Amdahl’s wet is een belangrijke wet in de computerwetenschap die de theoretische snelheidswinst van parallellisme beschrijft. De wet is geformuleerd door Gene Amdahl in 1967.
Het kernprincipe:
De snelheidswinst van parallellisme wordt beperkt door het deel van het programma dat niet geparallelliseerd kan worden. Dit is cruciaal om te begrijpen voordat je investeert in meer cores of parallel programmeren.
Als 90% van je programma parallel kan en je hebt oneindig veel cores, dan is de maximale snelheidswinst 10× (niet oneindig!). Dit komt omdat je nog steeds die 10% sequentieel moet uitvoeren.
De formule:
Waarbij:
\(S(n)\) = snelheidswinst met \(n\) cores
\(p\) = fractie van het programma dat parallel kan
\(n\) = aantal cores
Voorbeeld:
75% van je programma is te paralleliseren (\(p = 0.75\))
Je hebt 4 cores (\(n = 4\))
Je programma wordt 2.29× sneller, niet 4×!
Praktische implicatie: Wil je meer snelheidswinst? Dan moet je eerst proberen het sequentiële deel (25%) te verkleinen, voordat je meer cores toevoegt!
Oefening 9.2
Leg uit wat het verschil is tussen SIMD en MIMD.
Geef twee voorbeelden van taken die beter geschikt zijn voor:
SIMD
MIMD
Bereken de theoretische snelheidswinst met 8 cores voor een programma waarbij:
50% parallel kan
90% parallel kan
99% parallel kan
Waarom wordt een programma met 4 cores meestal niet exact 4× sneller?
Uitdaging: Zoek uit wat hyperthreading (of SMT - Simultaneous Multithreading) is en leg uit hoe dit verschilt van echte meerdere cores.
9.2. GPU: Graphics Processing Unit#
Een GPU (Graphics Processing Unit) is een gespecialiseerde processor die oorspronkelijk ontworpen was voor het renderen van graphics, maar tegenwoordig ook gebruikt wordt voor veel andere taken.
9.2.1. Waarom een aparte processor voor graphics?#
Het renderen van 3D graphics vereist enorm veel parallelle berekeningen. Voor een 4K-scherm (3840×2160 pixels) moet je bij 60 frames per seconde ongeveer 500 miljoen pixels per seconde berekenen! Voor elke pixel moet je:
3D coördinaten transformeren
Lichtberekeningen doen
Texturen toepassen
Schaduwberekeningen uitvoeren
Al deze berekeningen zijn onafhankelijk voor elke pixel en volgen hetzelfde patroon. Perfect voor massief parallellisme!
9.2.2. CPU vs GPU architectuur#
Je kunt een GPU eigenlijk zien als een extreme vorm van SIMD: in plaats van 4 of 8 data-elementen tegelijk te verwerken, verwerkt een GPU duizenden pixels of vertices tegelijk met dezelfde instructie. Maar de architectuur is wel fundamenteel anders dan een CPU.
CPU (Central Processing Unit):
Weinig cores (4-16 in consumentenprocessors)
Elke core is krachtig en complex
Goede branch prediction en out-of-order execution
Groot cache geheugen
Ontworpen voor latency (snelle individuele taken)
GPU (Graphics Processing Unit):
Duizenden simpele cores (bijv. NVIDIA RTX 4090: >16.000 cores!)
Elke core is eenvoudig
Weinig cache per core
Ontworpen voor throughput (veel taken tegelijk)
graph TD
subgraph CPU [CPU: 4 krachtige cores]
C1[Core 1<br/>Complex]
C2[Core 2<br/>Complex]
C3[Core 3<br/>Complex]
C4[Core 4<br/>Complex]
end
subgraph GPU [GPU: Duizenden eenvoudige cores]
G1[Core]
G2[Core]
G3[Core]
G4[Core]
G5[Core]
G6[...]
G7[...]
G8[...]
G1021[Core]
G1022[Core]
G1023[Core]
G1024[Core]
end
Analogie:
Een CPU is als een klein team van experts die complexe problemen kunnen oplossen. Een GPU is als een enorm leger van werkers die elk een simpele taak doen, maar dat wel heel snel en heel veel tegelijk.
9.2.3. GPU architectuur#
Een moderne GPU bestaat uit:
Streaming Multiprocessors (SM)
Een GPU bevat tientallen SM’s
Elke SM bevat tientallen tot honderdden cores
Elke SM heeft eigen shared memory en registers
Cores
Veel eenvoudiger dan CPU cores
Gespecialiseerd in floating point operaties
Werken in groepen (warps bij NVIDIA, wavefronts bij AMD)
Geheugen hiërarchie
Registers (zeer snel, per thread)
Shared memory (snel, per SM)
L1/L2 cache
VRAM (video RAM, groot maar trager)
9.2.4. SIMT: Single Instruction, Multiple Threads#
GPU’s gebruiken een model genaamd SIMT (Single Instruction, Multiple Threads). Dit lijkt op SIMD, maar met threads in plaats van alleen data.
Bij SIMT:
Groepen van threads (bijvoorbeeld 32 threads = 1 warp) voeren dezelfde instructie uit
Elke thread heeft eigen registers en kan eigen data hebben
Threads kunnen divergeren (verschillende paden nemen), maar dat is inefficiënt
Voorbeeld: Beeldbewerking
Je wilt een afbeelding blurren. Met SIMT:
Thread 0: Blur pixel (0,0)
Thread 1: Blur pixel (0,1)
Thread 2: Blur pixel (0,2)
...
Thread 1000: Blur pixel (10,10)
Alle threads voeren de blur functie uit, maar elk op een andere pixel. Duizenden pixels tegelijk!
9.2.5. GPGPU: General Purpose GPU#
Sinds circa 2006 kunnen GPU’s ook gebruikt worden voor niet-graphics taken. Dit heet GPGPU (General Purpose computing on GPU).
CUDA en OpenCL
Om GPU’s te programmeren gebruiken we frameworks zoals:
CUDA (NVIDIA, meest populair)
OpenCL (open standaard, werkt op verschillende GPU’s)
Metal (Apple)
DirectCompute (Microsoft)
Voorbeeldcode (simpel CUDA concept):
// GPU kernel - wordt uitgevoerd door duizenden threads
__global__ void vectorAdd(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]; // Elke thread doet 1 optelling
}
}
// Roep aan met 1000 threads
vectorAdd<<<10, 100>>>(A, B, C, 1000);
// 10 blocks × 100 threads = 1000 threads
9.2.6. Toepassingen van GPU computing#
Machine Learning / AI
Training van neurale netwerken (miljoenen matrixvermenigvuldigingen)
Deep learning frameworks (TensorFlow, PyTorch) gebruiken GPU’s
Een taak die 1 week duurt op CPU kan in uren op GPU
Wetenschappelijke berekeningen
Weermodellen en klimaatsimulaties
Moleculaire dynamica
Astronomische simulaties
Cryptocurrency mining
Hash-berekeningen (miljoenen per seconde)
Bitcoin, Ethereum (voorheen), etc.
Video rendering
Film special effects (Pixar, Industrial Light & Magic)
Real-time ray tracing in games
Dataverwerking
Database queries (GPU-databases)
Data analytics op grote datasets
Oefening 9.3
Leg uit waarom een GPU met 1000 eenvoudige cores vaak sneller is voor beeldbewerking dan een CPU met 8 krachtige cores.
Geef een voorbeeld van een taak die niet geschikt is voor een GPU en leg uit waarom niet.
Zoek op hoeveel cores de GPU in jouw computer (of game console) heeft. Vergelijk dit met het aantal cores van de CPU.
Waarom is het divergeren van threads in een GPU inefficiënt? (Tip: denk aan SIMT)
Programmeer uitdaging: Als je Python kent, probeer de bibliotheek
numbaofcupyte gebruiken om een eenvoudige berekening op een GPU uit te voeren (bijv. twee arrays optellen).
9.3. Tensor Processing Unit (TPU): AI-gespecialiseerde hardware#
Terwijl GPU’s al enorm snel zijn voor machine learning, zijn er nog gespecialiseerdere chips ontwikkeld speciaal voor kunstmatige intelligentie: Tensor Processing Units (TPU’s).
9.3.1. Wat is een TPU?#
Een TPU is een chip die door Google is ontworpen specifiek voor machine learning workloads, met name voor tensor operaties. Een tensor is een multi-dimensionale array van getallen - de fundamentele datastructuur in machine learning.
Voorbeelden van tensors:
Een getal: 0D tensor (scalar)
Een lijst: 1D tensor (vector)
[1, 2, 3]Een matrix: 2D tensor
[[1,2], [3,4]]Een afbeelding: 3D tensor (hoogte × breedte × kleurkanalen)
Een batch afbeeldingen: 4D tensor
9.3.2. Waarom nog meer specialisatie?#
CPU → GPU → TPU: steeds gespecialiseerder
Aspect |
CPU |
GPU |
TPU |
|---|---|---|---|
Doel |
Algemeen |
Graphics & parallel |
ML/AI |
Cores |
4-16 |
1.000-10.000+ |
Tensor cores |
Precisie |
64-bit float |
32-bit float |
16-bit, 8-bit, zelfs 4-bit |
Operatie |
Algemeen |
SIMT |
Matrix multiply |
Energie |
Medium |
Hoog |
Efficiënt voor ML |
De kracht van lagere precisie
Machine learning heeft vaak geen 64-bit precisie nodig. Door te werken met 16-bit of 8-bit getallen:
Kun je 4-8× meer berekeningen doen
Verbruik je 4-8× minder geheugen
Verbruik je minder energie
Wordt het sneller
TPU’s zijn geoptimaliseerd voor deze lagere precisie operaties.
9.3.3. Systolic array: het hart van een TPU#
Het belangrijkste onderdeel van een TPU is de systolic array - een 2D grid van simpele rekeneenheden die data doorpassen aan elkaar, zoals hartslag (systole) bloed rondpompt.
Hoe werkt een systolic array?
Stel je voor: een 4×4 grid van vermenigvuldigers voor matrixvermenigvuldiging.
Matrix A (input van links): Matrix B (input van boven):
[a1 a2 a3 a4] [b1]
[b2]
[b3]
[b4]
In de systolic array:
[×] → [×] → [×] → [×]
↓ ↓ ↓ ↓
[×] → [×] → [×] → [×]
↓ ↓ ↓ ↓
[×] → [×] → [×] → [×]
↓ ↓ ↓ ↓
[×] → [×] → [×] → [×]
↓ ↓ ↓ ↓
Results
Data stroomt van links en van boven naar binnen, wordt vermenigvuldigd en opgeteld, en stroomt door naar rechts en naar beneden. In een paar cycli is de hele matrixvermenigvuldiging klaar!
Waarom is dit zo efficiënt?
Data reuse: Elk getal wordt meerdere keren gebruikt zonder opnieuw uit geheugen te halen
Pipelining: Terwijl de ene berekening bezig is, begint de volgende al
Geen geheugen bottleneck: Data blijft in de array, minimale geheugen toegang
Energie-efficiënt: Simpele operaties, veel hergebruik
9.3.4. TPU generaties#
Google heeft meerdere generaties TPU’s ontwikkeld:
TPU v1 (2015)
Alleen inference (voorspellingen maken)
8-bit integer operaties
92 TeraOps/seconde
TPU v2 & v3 (2017-2018)
Ook training (modellen trainen)
Floating point support
Tot 420 TeraFlops
TPU v4 (2021)
2× sneller dan v3
Gebruikt in Google’s datacenters
TPU v5 (2023)
Nog krachtiger
Speciaal voor grote taalmodellen (GPT-achtig)
9.3.5. Andere AI accelerators#
TPU’s zijn niet de enige gespecialiseerde AI chips:
NVIDIA Tensor Cores
Ingebouwd in moderne NVIDIA GPU’s (vanaf Volta architectuur)
Geoptimaliseerd voor matrix operaties
Gebruikt in A100, H100 datacenter GPU’s
Google Edge TPU
Kleine, energiezuinige versie voor smartphones en IoT
Gebruikt in Google Pixel telefoons
Apple Neural Engine
Deel van Apple’s M-serie en A-serie chips
Tot 15.8 trillion operaties per seconde (M1)
Intel Nervana / Habana
Intel’s AI accelerators
Competitie voor NVIDIA en Google
AMD MI-serie
AMD’s datacenter GPU’s voor AI
Alternatief voor NVIDIA
9.3.6. Waar worden TPU’s gebruikt?#
Google’s eigen diensten:
Google Search (ranking, vertaling)
Google Photos (beeldherkenning)
Google Translate
Gmail (spam detectie, smart compose)
YouTube (aanbevelingen, ondertiteling)
Google Cloud:
Beschikbaar voor klanten via Google Cloud Platform
Training en inference van ML modellen
Vaak gebruikt voor:
Natuurlijke taalverwerking (NLP)
Computer vision
Aanbevelingssystemen
Vergelijking voor een typische ML-taak:
Training een groot taalmodel:
CPU: 1 maand
GPU: 1 week
TPU: 1-2 dagen
9.3.7. De toekomst: specialisatie voor AI#
We zien een trend naar steeds meer gespecialiseerde hardware:
Domain-Specific Architectures (DSA)
Chips voor specifieke taken
Nog efficiënter dan general-purpose
In-memory computing
Berekeningen doen waar data opgeslagen is. Nu wordt de data nog van het geheugen naar de verwerkingseenheid getransporteerd en weer terug.
Elimineert geheugen bottleneck
Neuromorphic chips
Geïnspireerd door de menselijke hersenen
Spiking neural networks
Extreem energie-efficiënt (Intel Loihi, IBM TrueNorth)
Quantum computing voor ML
Nog in onderzoeksfase
Potentieel voor bepaalde ML-problemen
Oefening 9.4
Leg uit waarom een TPU efficiënter is voor machine learning dan een GPU, ondanks dat een GPU meer algemene toepassingen heeft.
Waarom is lagere precisie (8-bit in plaats van 64-bit) vaak voldoende voor machine learning?
Zoek uit welke AI accelerator in jouw smartphone zit (als je er een hebt). Wat kan deze chip?
Leg uit wat het voordeel is van een systolic array voor matrixvermenigvuldiging.
Onderzoeksproject: Zoek informatie over “quantization” in machine learning en leg uit hoe dit samenhangt met de lage-precisie operaties van TPU’s.
Debat: Sommigen beweren dat gespecialiseerde chips zoals TPU’s de toekomst zijn, anderen denken dat general-purpose processors (met steeds betere software) voldoende zijn. Wat denk jij en waarom?
9.4. Harvard Architectuur en DSP#
Tot nu toe hebben we vooral gesproken over de von Neumann architectuur, waarbij instructies en data in hetzelfde geheugen zitten. Maar er is een alternatief: de Harvard architectuur.
9.4.1. Von Neumann vs Harvard#
Von Neumann architectuur (wat je al kent):
Eén geheugen voor instructies en data
Eén bus tussen processor en geheugen
Simpeler ontwerp
Gebruikt in de meeste computers
graph LR
CPU[CPU] <-->|Bus| MEM[Geheugen<br/>Instructies + Data]
Harvard architectuur:
Gescheiden geheugen voor instructies en data
Twee aparte bussen
Parallelle toegang mogelijk
Gebruikt in embedded systems en DSP’s
graph LR
CPU[CPU] <-->|Instructie bus| IMEM[Instructie<br/>Geheugen]
CPU <-->|Data bus| DMEM[Data<br/>Geheugen]
9.4.2. Voordelen van Harvard architectuur#
1. Parallelle toegang
Bij von Neumann kan de processor niet tegelijk een instructie en data ophalen (ze delen dezelfde bus). Bij Harvard kan dit wel!
Von Neumann:
Cyclus 1: Haal instructie op
Cyclus 2: Haal data op
Cyclus 3: Voer uit
Cyclus 4: Haal instructie op
...
Harvard:
Cyclus 1: Haal instructie op + haal data op (tegelijk!)
Cyclus 2: Voer uit + haal volgende instructie op (tegelijk!)
...
2. Geen von Neumann bottleneck
De bus tussen CPU en geheugen kan een bottleneck zijn. Met twee aparte bussen heb je dit probleem niet (of minder).
3. Verschillende geheugentypen
Je kunt verschillende soorten geheugen gebruiken:
Snel ROM voor instructies (niet-wijzigbaar)
Snel RAM voor data (wijzigbaar)
4. Veiligheid
Je kunt niet per ongeluk code overschrijven met data (of andersom). Dit voorkomt bepaalde types aanvallen.
9.4.3. Nadelen van Harvard architectuur#
1. Complexer
Je hebt twee bussen nodig, twee geheugensystemen, meer pinnen op de chip.
2. Minder flexibel
Je moet van tevoren weten hoeveel ruimte je nodig hebt voor instructies en voor data. Je kunt niet zomaar geheugen “verplaatsen” van het ene naar het andere.
3. Code kan niet zichzelf wijzigen
In sommige toepassingen (zoals just-in-time compilers) wil je code tijdens runtime genereren. Dit is moeilijker bij Harvard.
9.4.4. Modified Harvard architectuur#
Moderne processoren gebruiken vaak een modified Harvard architectuur:
Intern (in de processor) gescheiden caches voor instructies en data
Extern (buiten de processor) gedeeld geheugen
Dit combineert de voordelen van beide:
Snelheid van Harvard (parallelle cache toegang)
Flexibiliteit van von Neumann (gedeeld hoofdgeheugen)
9.4.5. DSP: Digital Signal Processor#
Een belangrijke toepassing van Harvard architectuur is de DSP (Digital Signal Processor). Dit is een gespecialiseerde processor voor signaalverwerking.
Wat is signaalverwerking?
Signaalverwerking gaat over het manipuleren van signalen:
Audio (geluid filteren, echo’s, compressie)
Video (filters, compressie, stabilisatie)
Telecommunicatie (modulatie, demodulatie)
Radar en sonar
Medische apparatuur (ECG, MRI verwerking)
Sensoren (beeldstabilisatie in camera’s)
Typische DSP operaties:
1. Convolutie
Convolutie is een wiskundige operatie waarbij je twee signalen combineert om een nieuw signaal te maken. Dit wordt veel gebruikt voor:
Filters: Bijvoorbeeld een blur filter op een foto - elk pixel wordt het gemiddelde van zijn buren
Echo’s en reverb: Het originele geluid wordt gemengd met vertraagde kopieën
Edge detection: In beeldverwerking om randen te vinden
Wat betekent de formule?
De formule \(y[n] = \sum_{k} x[k] \cdot h[n-k]\) zegt eigenlijk:
\(x[k]\) is het ingangssignaal (bijvoorbeeld geluid of een rij pixels)
\(h[n-k]\) is de “impulse response” of filter-kern (bijvoorbeeld: hoeveel echo, of welke pixels tel je mee)
Voor elke output positie \(n\) tel je alle \(x[k]\) waarden op, gewogen met \(h[n-k]\)
Simpel voorbeeld: Gemiddelde van 3 getallen
Ingang: [5, 10, 15, 20, 25]
Filter: [1/3, 1/3, 1/3] (gemiddelde van 3)
Output: [10, 15, 20] (elk getal is het gemiddelde van 3 buren)
Fig. 9.1 Convolutie: elk output sample is een gewogen som van input samples#
2. FFT (Fast Fourier Transform)
De Snelle Fourier-transformatie zet een signaal om van het tijdsdomein naar het frequentiedomein. Dit klinkt ingewikkeld, maar het idee is simpel:
Tijdsdomein: Je ziet hoe een signaal verandert in de tijd Frequentiedomein: Je ziet welke frequenties (tonen) in het signaal zitten
Toepassingen:
MP3 compressie: Verwijder frequenties die mensen niet kunnen horen
JPEG compressie: Verwijder hoge frequenties (details) die je oog niet ziet
Stemherkenning: Analyseer welke tonen iemand uitspreekt
Equalizer: Pas specifieke frequenties aan (bas, midden, hoog)
Fig. 9.2 FFT zet een tijdsignaal om naar frequenties#
3. Correlatie
Correlatie meet hoe vergelijkbaar twee signalen zijn. Het is vergelijkbaar met convolutie, maar dan zoek je naar overeenkomsten.
Toepassingen:
Patroonherkenning: Zoek een specifiek geluid in een audio-opname
GPS synchronisatie: Vind het juiste tijdstip in satelliet signalen
Barcode/QR-code scanning: Herken eenvoudige patronen in camera beelden
Radar en sonar: Detecteer een echo van een uitgezonden puls
Voorbeeld: Zoek de melodie “do-re-mi” in een lange muziekopname. Correlatie geeft een hoge waarde waar de melodie voorkomt.
Fig. 9.3 Correlatie vindt patronen in signalen#
4. FIR/IIR filters
Digitale filters zijn essentieel in signaalverwerking om bepaalde frequenties te behouden of te verwijderen.
FIR (Finite Impulse Response)
Gebruikt alleen input samples
Altijd stabiel (kan niet “ontploffen”)
Gebruikt voor: lineaire-fase filters, equalizers
IIR (Infinite Impulse Response)
Gebruikt ook eerdere output samples (heeft “geheugen”)
Efficiënter (minder berekeningen)
Gebruikt voor: bas/treble filters, compressors
Toepassingen:
Audio equalizer: Verhoog bas, verlaag treble
Noise cancellation: Filter achtergrondgeluid eruit
Anti-aliasing: Voorkom vervelende artefacten bij sampling
Beeldverwerking: Blur, sharpen, edge detection
Fig. 9.4 FIR en IIR filters in actie#
9.4.6. DSP architectuur kenmerken#
Een typische DSP heeft:
1. Harvard architectuur
Parallelle toegang tot instructies en data
Cruciaal voor real-time verwerking
2. MAC-eenheid (Multiply-Accumulate)
De meest voorkomende operatie in signaalverwerking is:
accumulator += a * b
Een DSP kan dit in één cyclus:
MAC: acc = acc + (x * y) // 1 cyclus!
Een normale CPU zou nodig hebben:
MULT temp, x, y // 1 cyclus
ADD acc, acc, temp // 1 cyclus
3. Speciale adresseringsmodi
Circulaire buffers: voor audio buffers die constant worden opgevuld
buffer[i++ % N] // automatisch in hardware!
Bit-reversed addressing: voor efficiënte FFT
4. Zero-overhead loops
Loops zonder extra cycli voor loop-controle (teller, vergelijking, sprong)
5. Fixed-point arithmetic
Veel DSP’s gebruiken fixed-point (vaste komma) in plaats van floating point:
Sneller
Minder energie
Voldoende precisie voor veel toepassingen
9.4.7. Voorbeelden van DSP’s#
Texas Instruments TMS320 serie
Een van de populairste DSP families
Gebruikt in audio apparatuur, telecom, automotive
Analog Devices SHARC
Hoge prestaties
Gebruikt in professionele audio apparatuur
ARM Cortex-M met DSP extensions
DSP mogelijkheden in een microcontroller
Gebruikt in embedded systems (drones, wearables)
Qualcomm Hexagon
DSP in Snapdragon processors (smartphones)
Voor camera ISP, audio, sensoren
9.4.8. DSP in je telefoon#
Je smartphone heeft waarschijnlijk meerdere DSP’s:
Audio DSP
Noise cancellation bij telefoongesprekken
Equalizer, bass boost
Voice recognition (“Hey Siri”, “OK Google”)
Image Signal Processor (ISP)
RAW sensor data → JPEG
Auto-focus, auto-exposure
Face detection
HDR, night mode
Modem DSP
4G/5G signaalverwerking
Modulatie, demodulatie
Error correction
Sensor Hub
Verwerking van accelerometer, gyroscoop, kompas
Stappenteller
Altijd aan, zeer energie-efficiënt
Oefening 9.5
Leg uit waarom de Harvard architectuur sneller kan zijn dan von Neumann voor real-time signaalverwerking.
Waarom heeft een DSP speciale hardware voor multiply-accumulate (MAC) operaties?
Geef drie voorbeelden van signaalverwerking die nu in jouw smartphone plaatsvindt.
Leg uit waarom een circulaire buffer nuttig is voor audio-verwerking.
Onderzoeksproject: Zoek uit wat een “Image Signal Processor” (ISP) doet in een smartphone camera en waarom dit geen normale CPU is.
Programmeer uitdaging: Implementeer een simpel lopend gemiddelde filter in je favoriete programmeertaal:
output[n] = (input[n] + input[n-1] + input[n-2]) / 3
Test het op een array van random getallen.
9.5. Cache theorie: snelheidswinst door slim geheugen#
Een van de grootste uitdagingen in moderne computers is de enorme snelheidsverschil tussen de processor en het hoofdgeheugen. Dit heet de memory wall of von Neumann bottleneck. De oplossing: cache geheugen.
TODO: afbeelding von Neumann architectuur
9.5.1. Het snelheidsprobleem#
Processor snelheid vs geheugen snelheid
Een moderne processor draait op bijvoorbeeld 3 GHz (3 miljard cycli per seconde). Een geheugenoperatie duurt ongeveer 100-200 cycli. Als de processor bij elke instructie op het geheugen moet wachten, staat hij 99% van de tijd stil!
Vergelijking:
Processor: sportwagen (300 km/h)
RAM: vrachtwagen (30 km/h)
Harde schijf: wandelaar (3 km/h)
graph TD
CPU[CPU<br/>3 GHz<br/>~0.3 ns]
L1[L1 Cache<br/>~1 ns]
L2[L2 Cache<br/>~3-10 ns]
L3[L3 Cache<br/>~10-20 ns]
RAM[RAM<br/>~100 ns]
SSD[SSD<br/>~50,000 ns]
HDD[HDD<br/>~5,000,000 ns]
CPU --> L1
L1 --> L2
L2 --> L3
L3 --> RAM
RAM --> SSD
SSD --> HDD
style CPU fill:#f99
style L1 fill:#ff9
style L2 fill:#9f9
style L3 fill:#9ff
style RAM fill:#99f
style SSD fill:#bbf
style HDD fill:#ddf
9.5.2. Wat is cache?#
Cache is een klein, snel geheugen tussen de processor en het hoofdgeheugen. Het bevat kopieën van vaak gebruikte data uit het langzamere hoofdgeheugen. Wanneer een CPU data nodig heeft uit het geheugen en het is opgeslagen in de cache, kan de data sneller naar de CPU verstuurd worden vanuit de cache. Dit is dus een mooie tijdwinst.
Cache hiërarchie
Moderne processors hebben meestal 3 niveaus (levels) cache:
L1 Cache (Level 1)
Klein (32-64 KB per core)
Zeer snel (1-2 cycli)
Vaak gesplitst: L1i (instructies) en L1d (data) → Harvard!
Direct in de processor core
L2 Cache (Level 2)
Middelgroot (256 KB - 1 MB per core)
Snel (10-20 cycli)
Meestal ook per core
L3 Cache (Level 3)
Groot (8-64 MB)
Langzamer (30-70 cycli), maar nog steeds veel sneller dan RAM
Gedeeld tussen alle cores
9.5.3. Hoe werkt cache?#
Basis principe: lokaliteit
Cache werkt goed door twee vormen van lokaliteit:
1. Temporal locality (tijdslokaliteit)
Als je data net gebruikt hebt, gebruik je het waarschijnlijk binnenkort weer.
Voorbeeld:
sum = 0
for i in range(1000):
sum += i # variabele 'sum' wordt steeds opnieuw gebruikt
Wanneer de variabele sum in de cache zit, hoeft deze dus niet steeds uit het geheugen gehaald te worden. Nog sneller zou natuurlijk zijn om de variabele sum in een register te zetten, maar dat is niet altijd mogelijk.
2. Spatial locality (ruimtelijke lokaliteit)
Als je data op adres X gebruikt, gebruik je waarschijnlijk ook data op adres X+1, X+2, etc.
Voorbeeld:
for i in range(1000):
array[i] = 0 # opeenvolgende array elementen
Het voordeel van spatial locality
Spatial locality zorgt voor veel cache hits omdat de cache werkt met cache lines: blokken van bijvoorbeeld 64 bytes die in één keer uit RAM worden geladen. Als je één byte nodig hebt, worden automatisch ook de 63 bytes erna in de cache geladen.
Voorbeeld: 2D array traversal
Stel je hebt een matrix van 1000×1000 integers (4 bytes per integer). Cache line grootte is 64 bytes = 16 integers.
Methode 1: Rij voor rij (goede spatial locality)
for i in range(1000):
for j in range(1000):
matrix[i][j] = 0 # [0][0], [0][1], [0][2], ...
In het geheugen liggen rij-elementen naast elkaar. Bij toegang tot matrix[0][0] worden matrix[0][0] t/m matrix[0][15] in de cache geladen. De volgende 15 toegangen zijn dus cache hits!
Eerste toegang per 16: cache miss
Volgende 15 toegangen: cache hits
Hit rate: ~94% (15/16)
Methode 2: Kolom voor kolom (slechte spatial locality)
for j in range(1000):
for i in range(1000):
matrix[i][j] = 0 # [0][0], [1][0], [2][0], ...
Nu spring je tussen rijen: matrix[0][0], dan matrix[1][0], dan matrix[2][0], etc. Elke rij ligt 4000 bytes verder in het geheugen. De cache line die je laadt bij matrix[0][0] (met matrix[0][0] t/m matrix[0][15]) gebruik je niet voor de volgende toegang!
Bijna elke toegang: cache miss
Hit rate: ~5-10%
Time difference:
Methode 1: 1 miljoen × (6% × 100ns + 94% × 2ns) = 7.9 ms
Methode 2: 1 miljoen × (95% × 100ns + 5% × 2ns) = 95.1 ms
Methode 1 is 12× sneller door goede spatial locality! Dit is waarom programmeurs altijd proberen data sequentieel te benaderen.
9.5.4. Cache hits en misses#
Cache hit: Wanneer de door de CPU opgevraagde data in de cache zit, noemen we dit een cache hit. Met zo’n hit is de data dus erg snel toegankelijk voor de CPU en worden er geen kostbare cycles gewacht op data. Cache miss: In het geval dat de door de CPU opgevraagde data niet in de cache is opgeslagen, dan spreken we van een cache miss. Zo’n miss zorgt ervoor dat de data uit het tragere RAM geheugen moet komen.
Hit rate: Je kunt de kwaliteit van de afstemming van de programmacode op de cache meten met de zogenaamde hit rate. Dit is het percentage van de memory toegangen dat in cache zit. Je zult dit misschien herkennen als een procent-berekening van wiskunde. Nou, dat is het precies :)
Hit rate = hits / (hits + misses)
Voorbeeld:
95 hits, 5 misses uit 100 toegangen
Hit rate = 95 / 100 = 95%
Gemiddelde toegangstijd: Wanneer je de hitrate weet, kun je ook de gemiddelde toegangstijd voor een geheugenoperatie berekenen. Je hebt hiervoor de hit rate en miss rate nodig. De miss rate bereken je als miss rate = 100 - hit rate. Daarnaast heb je de toegangstijden voor de cache en het RAM geheugen nodig. Wanneer je een berekening wil maken voor een CPU zonder enige cache, dan zet je de hit rate op 0 en de miss rate op 100.
Average = (hit rate × cache tijd) + (miss rate × RAM tijd)
Voorbeeld:
Cache: 2 ns
RAM: 100 ns
Hit rate: 95%
Average = (0.95 × 2) + (0.05 × 100)
= 1.9 + 5
= 6.9 ns
Zonder cache zou elke toegang 100 ns duren. Met cache is het ~14× sneller!
9.5.5. Cache mapping strategieën#
Het rekenen en inzien dat een cache daadwerkelijk tijdwinst op kan leveren is mooi en aardig, maar hoe besluit de cache welke data op te slaan en waar? Dat kan grofweg op drie verschillende manieren.
1. Direct Mapped Cache
Elk geheugenadres kan op maar één plek in de cache.
Cache positie = (adres) % (cache grootte)
Dus in een rekenvoorbeeld: stel dat je een (superkleine) cache hebt van 8 bytes. Dan komen RAM-adressen 0, 8, 16, ... op plek 0 in de cache. RAM-adressen 1, 9, 17, ... staan op plek 1 in de cache, enzovoorts.
Het voordeel van deze methode is dat het mappen van de cache heel snel kan plaats vinden omdat het een ‘simpele’, haast naieve methode is. Het nadeel is dat het veel cache conflicts kan veroorzaken.
Een cache conflict is wanneer verschillende RAM-adressen concurreren om dezelfde cache plek. Hier is een concreet voorbeeld van wat er misgaat:
Scenario: Je programma wisselt constant tussen RAM-adres 0 en RAM-adres 8:
1. Lees adres 0 → cache positie 0 (0 % 8 = 0) → MISS, laad in cache
2. Lees adres 8 → cache positie 0 (8 % 8 = 0) → MISS, gooi adres 0 eruit, laad adres 8
3. Lees adres 0 → cache positie 0 (0 % 8 = 0) → MISS, gooi adres 8 eruit, laad adres 0
4. Lees adres 8 → cache positie 0 (8 % 8 = 0) → MISS, gooi adres 0 eruit, laad adres 8
...
Resultaat: 100% cache misses, terwijl 7 van de 8 cache posities leeg blijven! Dit is het probleem van een direct mapped cache: twee veelgebruikte adressen kunnen elkaar constant uit de cache gooien.
Samengevat
Voordeel: Simpel, snel
Nadeel: Veel cache conflicts (verschillende adressen concurreren om dezelfde cache plek)
2. Fully Associative Cache
Elk geheugenadres kan op elke willekeurige plek in de cache.
Bij deze methode wordt voor elke cache-plek een tag bijgehouden die aangeeft welk RAM-adres erin zit. Bij elke cache-toegang moeten alle tags gecontroleerd worden om te zien of het gezochte adres in de cache staat. Dit heet een associative search. Dit zorgt natuurlijk voor vertraging. Dus dan is de CPU weer aan het wachten op data. En dat was nou precies wat we met de cache wilden oplossen. Een absoluut voordeel aan deze manier van organiseren, is dat je geen enkele cache miss krijgt.
Samengevat
Voordeel: Geen conflicts, beste hit rate
Nadeel: Complex, traag om te zoeken
3. N-way Set Associative Cache (meest gebruikt)
De N-way set associative cache combineert het beste van beide vorige methoden. De cache wordt opgedeeld in sets, en elk geheugenadres heeft een vaste set waar het in kan - maar binnen die set mag het op elke vrije plek staan.
Voorbeeld: 4-way set associative cache van 8 sets
Set = (adres) % (aantal sets) → bepaalt welke set (snel, zoals direct mapped)
Binnen de set: keuze uit 4 plekken → voorkomt conflicts (zoals fully associative)
Terug naar het conflict-voorbeeld: RAM-adressen 0 en 8 komen nu allebei in set 0, maar die set heeft 4 plekken. Ze kunnen dus naast elkaar in de cache bestaan zonder elkaar eruit te gooien!
Set 0: [adres 0 | adres 8 | leeg | leeg] ← geen conflict meer!
Set 1: [leeg | leeg | leeg | leeg]
...
Pas als er 5 of meer adressen in dezelfde set zitten, ontstaat er een conflict. De kans daarop is bij 4 plekken per set veel kleiner dan bij direct mapped.
De N in “N-way” staat voor het aantal plekken per set. Een 2-way cache heeft 2 plekken per set, een 8-way cache heeft er 8. Moderne processors gebruiken vaak 4-way of 8-way set associative caches als L1, en 16-way voor L3.
Samengevat
Voordeel: Goede balans tussen snelheid en hit rate
Nadeel: Complexer dan direct mapped
9.5.6. Cache replacement policies#
Als de cache vol is, welke data gooi je eruit?
LRU (Least Recently Used)
Gooi de data eruit die het langst niet gebruikt is
Meest gebruikt in praktijk
Goede prestaties voor de meeste programma’s. Klinkt eigenlijk ook best logisch. Heb je iets lang niet meer gebruikt, dan is de kans klein dat je het binnenkort nodig hebt. Behalve die ene kabel uit die doos met oude kabels (IYKYK).
FIFO (First In, First Out)
Gooi de oudste data eruit. De ouderdom wordt op basis van tijdstip van toevoeging bepaald en niet zozeer door het laatste gebruik (zoals de LRU hierboven werkt).
Simpeler dan LRU
Iets slechtere prestaties.
Random
Kies willekeurige data om eruit te gooien
Zeer simpel
Soms verrassend goed
LFU (Least Frequently Used)
Gooi data eruit die het minst gebruikt is
Complexer
Kan beter zijn voor bepaalde workloads (een database buffercache of een Web/CDN cache).
9.5.7. Write policies#
Wat gebeurt er als je data in cache wijzigt? Je hebt twee instanties van de data. Eentje in het RAM-geheugen en eentje in de cache. Wat je te alle tijde wil voorkomen is dat er een mismatch is tussen RAM en cache, zonder dat dit bekend is. Dus wanneer de CPU data wil opslaan, zal dit eerst in de cache gewijzigd worden. Dan zijn er twee keuzes te maken over wanneer je de data terugschrijft naar RAM. Direct of uitgesteld.
Write-through (direct)
Schrijf naar cache EN direct naar RAM
Voordeel: RAM is altijd up-to-date
Nadeel: Langzamer (elke write gaat naar RAM). Voor read-heavy workloads maakt dit niet veel uit.
Write-back (uitgesteld)
Schrijf alleen naar cache, pas later naar RAM
Voordeel: Sneller
Nadeel: Deze aanpak is complexer, want je per cache-entry bijhouden of de cache data gewijzigd is. Dit gebeurt middels een dirty bit, die aangeeft of de cache-data gewijzigd is. Dan weet je dat die data nog naar de RAM geschreven moet worden.
9.5.8. Cache coherency (multi-core)#
We zijn er tot nu toe van uit gegaan dat er slechts 1 CPU betrokken is bij de cache. Dus is er ook maar 1 CPU, die schrijven en lezen in het geheugen via de cache. Wat nou als je meerdere CPU’s of cores hebt? En wat nou als die cores ieder een eigen cache hebben. Hoe hou je de boel gesynchroniseerd?
Probleem:
Core 1: Leest X = 5 (in L1 cache)
Core 2: Leest X = 5 (in L1 cache)
Core 1: Schrijft X = 10 (in zijn L1)
Core 2: Leest X... krijgt 5! (verouderd!)
Oplossing: Cache coherence protocols
MESI protocol (meest gebruikt):
Modified: Deze cache heeft de enige geldige kopie (en hij is gewijzigd)
Exclusive: Deze cache heeft de enige kopie (maar niet gewijzigd)
Shared: Meerdere caches hebben een kopie
Invalid: Deze cache heeft een verouderde kopie
Bij een write naar cache in Modified/Exclusive mode:
Invalideer kopieën in andere caches
Forceer ze opnieuw uit RAM te halen
9.5.9. False sharing#
Een subtiel multi-core probleem:
struct {
int counter1; // gebruikt door core 1
int counter2; // gebruikt door core 2
} data;
Ook al gebruiken de cores verschillende variabelen, ze zitten in dezelfde cache line (typisch 64 bytes). Elke write door één core invalideert de cache van de andere!
Oplossing: padding
struct {
int counter1;
char padding[60]; // Forceer verschillende cache lines
int counter2;
} data;
Het detecteren van false sharing doe je door middel van een profiler. Daarmee kun je de prestatie van code fragmenten meten. De toepassingsgebieden, waarin er actief gezocht wordt naar false sharing bij multi-core performance zijn game engines, database engines, high frequency trading en operating system kernels. De padding-oplossing wordt ook pas gebruikt, nadat er gemeten is!
9.5.10. Cache als beveiligingsrisico: Spectre en Meltdown#
De cache is gedeelde hardware tussen processen. Dat maakt het ook een doelwit voor aanvallen. In 2018 werden twee van de meest impactvolle CPU-kwetsbaarheden ooit ontdekt: Meltdown en Spectre.
Het aanvalsprincipe: timing als zijkanaal
De aanval maakt gebruik van speculatieve uitvoering (de CPU voert alvast instructies uit die misschien niet nodig zijn) én de cache:
Verleid de CPU om speculatief verboden data te lezen — data die eigenlijk van een ander proces of de kernel is
Die verboden data landt in de cache, ook al wordt de instructie achteraf door de CPU teruggedraaid
Meet hoe lang geheugentoegangen duren: een snelle toegang = cache hit = die data was al geladen
Zo kun je bit voor bit verboden geheugen aflezen via timing, zonder het ooit formeel te “lezen”
Waarom is dit zo ernstig?
Een normaal programma kan in principe het geheugen van andere programma’s of de kernel niet lezen — het besturingssysteem en de hardware voorkomen dat. Maar via dit zijkanaal omzeil je die bescherming volledig, zonder dat de CPU een foutmelding geeft.
De fix en de prijs
De oplossing heet KPTI (Kernel Page-Table Isolation): de page tables van de kernel en van gebruikersprogramma’s worden strikt gescheiden gehouden. Dit sluit het zijkanaal, maar kost wel ~5-30% performance — elke keer dat een programma de kernel aanroept (voor bijvoorbeeld bestandstoegang of netwerk) moet de CPU een dure context-switch maken.
Dit is een mooi voorbeeld van de spanning tussen prestatie en veiligheid in hardware-ontwerp. De speculatieve uitvoering die CPU’s zo snel maakt, blijkt ook een beveiligingsrisico te zijn.
9.5.11. Cache-friendly programmeren#
Je kunt code schrijven die beter gebruik maakt van cache:
Goed: Spatial locality
# Rijen eerst (cache-friendly)
for i in range(1000):
for j in range(1000):
matrix[i][j] = 0
Slecht: Cache misses
# Kolommen eerst (cache-unfriendly voor row-major arrays)
for j in range(1000):
for i in range(1000):
matrix[i][j] = 0
In het eerste geval lees je opeenvolgende memory locaties (cache hit!). In het tweede geval spring je steeds door het geheugen (cache miss!).
Loop tiling/blocking
Voor grote data: verdeel in kleine blokken die in cache passen.
# Zonder blocking
for i in range(N):
for j in range(N):
for k in range(N):
C[i][j] += A[i][k] * B[k][j]
# Met blocking (cache-friendly)
for ii in range(0, N, BLOCK):
for jj in range(0, N, BLOCK):
for kk in range(0, N, BLOCK):
for i in range(ii, min(ii+BLOCK, N)):
for j in range(jj, min(jj+BLOCK, N)):
for k in range(kk, min(kk+BLOCK, N)):
C[i][j] += A[i][k] * B[k][j]
Oefening 9.6
Leg uit waarom een computer met cache geheugen veel sneller is dan zonder, ook al is de CPU even snel.
Bereken de gemiddelde toegangstijd voor:
L1 cache: 2 ns, hit rate 90%
L2 cache: 10 ns, hit rate 95% (van L1 misses)
RAM: 100 ns
Waarom is het lopen door een array rij-voor-rij meestal sneller dan kolom-voor-kolom?
Leg uit wat het verschil is tussen write-through en write-back cache.
Experiment: Schrijf een programma dat een grote 2D array doorloopt op twee manieren (rij-eerst vs kolom-eerst) en meet het tijdsverschil. Gebruik bij voorkeur een gecompileerde taal (C, C++, Rust, GoLang) en niet een geinterpreteerde taal als JavaScript of Python.
Onderzoeksproject: Zoek uit hoeveel L1, L2 en L3 cache jouw processor heeft. Gebruik tools zoals
lscpu(Linux/Mac) of CPU-Z (Windows).Uitdaging: Leg uit waarom “loop tiling” de cache hit rate kan verbeteren bij matrix vermenigvuldiging.
9.6. Afsluiting#
In dit verdiepingshoofdstuk heb je geavanceerde computerarchitectuur concepten geleerd:
SIMD en MIMD: Verschillende vormen van parallellisme en hoe ze gebruikt worden
GPU’s: Massief parallelle processors voor graphics en general-purpose computing
TPU’s: Gespecialiseerde hardware voor machine learning met systolic arrays
Harvard architectuur en DSP’s: Alternatieve architecturen voor gespecialiseerde toepassingen
Cache theorie: Hoe geheugen hiërarchieën computers snel maken ondanks de memory wall
Deze concepten zijn fundamenteel voor het begrijpen van moderne computing, van je smartphone tot supercomputers en AI-systemen. De trend is duidelijk: steeds meer specialisatie voor specifieke taken, gecombineerd met slimme geheugen hiërarchieën om de kloof tussen processor en geheugen te overbruggen.
In een vervolgopleiding zul je hier nog veel dieper op ingaan, met bijvoorbeeld vakken als computerarchitectuur, parallel computing, en hardware design!