Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Cache-Simulation in C: Fünf Schlüsselfunktionen für die COMP9414 AI Aufgabe 1

Lerne, wie du die fünf fehlenden Funktionen zur Cache-Simulation in C implementierst – basierend auf der COMP9414 Aufgabe 1. Mit praxisnahen Beispielen und aktuellen Bezügen zu KI und Gaming.

Cache-Simulation C COMP9414 Aufgabe 1 Matrix-Multiplikation Cache Cache-Hierarchie Implementierung setSizesOffsetsAndMaskFields getindex gettag writeback fill Caching in KI Cache-Optimierung Gaming 64-Bit Cache Simulation performaccess Funktion Cache-Parameter berechnen CSE Lab Cache Projekt Cache-Miss und Fill Writeback Strategie Cache-Assoziativität Blockgröße Cache

Einleitung: Warum Cache-Simulation für KI relevant ist

In der heutigen KI-Welt, in der große Sprachmodelle wie GPT-5 oder Bildgeneratoren Milliarden von Matrix-Operationen pro Sekunde ausführen, ist das Verständnis von Cache-Hierarchien entscheidend. Die COMP9414 Aufgabe 1 verlangt, eine Cache-Simulation für Matrix-Multiplikationen zu implementieren – ein Kernproblem, das auch in modernen KI-Beschleunigern wie GPUs und TPUs auftritt. In diesem Tutorial lernst du, wie du die fünf fehlenden Funktionen in YOURCODEHERE.c korrekt implementierst, ohne die Framework-Dateien zu ändern.

Überblick über die Aufgabenstellung

Das Projekt simuliert eine parameterisierte Cache-Hierarchie für naive O(N³) Matrix-Multiplikation. Die Argumente in der Makefile definieren Größe der Matrix, Anzahl der Multiplikationen und Cache-Parameter (Level, Größe, Assoziativität, Blockgröße). Deine Aufgabe: Implementiere die fünf Stub-Funktionen setSizesOffsetsAndMaskFields, getindex, gettag, writeback und fill in YOURCODEHERE.c. Der Rest des Frameworks (csim.c, csim.h) darf nicht geändert werden.

1. setSizesOffsetsAndMaskFields – Die Grundlagen konfigurieren

Diese Funktion berechnet aus den Cache-Parametern (Größe, Assoziativität, Blockgröße) die Anzahl der Sets, die Offset-Bits, die Index-Bits und die Tag-Bits. In einer 64-Bit-Umgebung (wie den CSE-Labormaschinen) ist ein Wort 8 Bytes. Die Blockgröße ist ein Vielfaches von 8 Bytes. Die Formeln lauten:

  • Anzahl der Blöcke = Cache-Größe / Blockgröße
  • Anzahl der Sets = Anzahl der Blöcke / Assoziativität
  • Offset-Bits = log2(Blockgröße)
  • Index-Bits = log2(Anzahl der Sets)
  • Tag-Bits = 64 - Index-Bits - Offset-Bits (bei 64-Bit-Adressen)

Beispiel: Bei einer 32-KB-Cache-Größe, 8-fach Assoziativität und 64-Byte-Blöcken ergibt sich: Anzahl Blöcke = 32768/64 = 512, Sets = 512/8 = 64, Offset-Bits = 6, Index-Bits = 6, Tag-Bits = 52. Diese Werte speicherst du in der Cache-Struktur (z.B. cache->numSets, cache->offsetBits usw.).

2. getindex – Den Index aus der Adresse extrahieren

Die Funktion getindex extrahiert den Set-Index aus einer Speicheradresse. Der Index liegt zwischen den Offset-Bits und den Tag-Bits. Maskiere die Adresse mit einer Maske, die nur die Index-Bits enthält, und verschiebe sie um die Offset-Bits nach rechts. Beispiel: Bei 6 Offset-Bits und 6 Index-Bits ist die Maske 0x3F << 6 (0xFC0). Der Index = (Adresse & Maske) >> 6.

3. gettag – Das Tag aus der Adresse extrahieren

Das Tag sind die höherwertigen Bits oberhalb von Index und Offset. Verschiebe die Adresse um (Index-Bits + Offset-Bits) nach rechts. Beispiel: Bei 6+6=12 Bits: tag = Adresse >> 12.

4. writeback – Daten in die nächste Hierarchieebene schreiben

Writeback wird aufgerufen, wenn ein Cache-Block ersetzt wird und die Daten modifiziert sind (Dirty-Bit gesetzt). Die Funktion muss die Daten aus dem lokalen Cache in den nächsthöheren Cache (oder Hauptspeicher) schreiben. Dazu rufst du performaccess mit der entsprechenden Adresse und den Daten auf. Die Funktion performaccess erwartet: Adresse, Größe (hier 8), Zeiger auf Daten (lesen/schreiben), und ein Flag für Schreibzugriff. Achte darauf, das Dirty-Bit nach dem Schreiben zurückzusetzen.

5. fill – Daten aus der nächsten Hierarchieebene holen

Fill wird bei einem Cache-Miss aufgerufen, um den fehlenden Block aus dem nächsthöheren Cache (oder Hauptspeicher) zu laden. Rufe performaccess mit Lese-Flag auf und speichere die erhaltenen Daten im lokalen Cache-Block. Setze das Valid-Bit und lösche das Dirty-Bit.

Vollständiges Code-Beispiel (Auszug)

void setSizesOffsetsAndMaskFields(cache_t *cache) {
    int blockSize = cache->blockSize;
    int cacheSize = cache->size;
    int assoc = cache->assoc;
    cache->numBlocks = cacheSize / blockSize;
    cache->numSets = cache->numBlocks / assoc;
    cache->offsetBits = log2(blockSize);
    cache->indexBits = log2(cache->numSets);
    cache->tagBits = 64 - cache->indexBits - cache->offsetBits;
    cache->setMask = (cache->numSets - 1) << cache->offsetBits;
    cache->tagShift = cache->indexBits + cache->offsetBits;
}

Testen mit make test

Nach der Implementierung sollte make test alle Testfälle durchlaufen. Der erste Test (ohne Cache) läuft sofort, der zweite (mit Cache) verursachte vorher einen Segfault – nun sollte er funktionieren. Die erwartete Laufzeit beträgt 1-2 Minuten, hauptsächlich für den letzten Test.

Häufige Fehler und Tipps

  • Falsche Bit-Berechnungen: Überprüfe die log2-Funktion – verwende eine Lookup-Tabelle oder Bit-Manipulation.
  • Vergessen, performaccess korrekt aufzurufen: Die Größe ist immer 8 (ein Wort).
  • Nichtbeachtung der 64-Bit-Architektur: Alle Adressen und Masken müssen 64-Bit sein (uint64_t).
  • Modifikation anderer Dateien: Nur YOURCODEHERE.c darf geändert werden.

Fazit

Mit dieser Anleitung bist du in der Lage, die fünf Kernfunktionen der Cache-Simulation zu implementieren. Dieses Wissen ist nicht nur für die COMP9414 Aufgabe 1 nützlich, sondern auch für Optimierungen in KI-Anwendungen, Spiele-Engines oder Datenbanken, wo Cache-Effizienz über Performance entscheidet. Viel Erfolg beim Testen!