So, ja, also einmal nur kurze Info,
seit heute sind sowohl bei uns auf der Webseite als auch bei Stine,
die Foliensätze jetzt, also quasi in Komplettversion,
zum Runterladen verfügbar, einmal quasi das,
was ich jetzt also hier immer vorgespielt habe, ansonsten,
wenn man sich das jetzt so ein bisschen kompakter durchlesen will,
beziehungsweise für, falls man vorhat,
also hier irgendwie was, für die Papierindustrie zu tun,
gibt es dann also auch noch eine Version,
wo dann also quasi zwei Vorlesungsfolien auf einer A4-Seite drauf sind,
war es dann, sagen wir mal,
so ein bisschen Druckformat freundlicher ist.
Gut, also wie gesagt, sowohl hier bei uns auf der Webseite
als auch bei Szene an üblicher Stelle bei den Rechnerstrukturen anzufinden.
Ja, heute großes Finale, wie gesagt,
ich werde hier also auch nur so ein paar Sachen noch kurz vorstellen, einiges überspringen,
muss man sehen, wie weit ich dann die Zeit überhaupt jetzt ausnutze. Ich hatte ja aufgehört im Prinzip,
So ein bisschen mit dieser Motivation zur Speicherhierarchie, wo man sieht,
also wie sich jetzt eigentlich geschwindigkeitsmäßig quasi Prozessoutakt zu S-Rahmen-Zugriffsgeschwindigkeit,
also sprich das, was im Prinzip dann Cashes sind,
zum Hauptspeicherzugriffsgeschwindigkeit und eben Zukaufsgeschwindigkeiten von Platten,
also irgendwie Verhalten, wo man dann eben sagen kann,
also von CPU-Tag verglichen zum Hauptspeicher ist,
man hat also schon von einem Faktor von irgendwie deutlich über 100 und in Bezug auf irgendwie sowas wie Festplatten,
muss er daneben sagen,
dass man da dann also im Bereich von einigen hunderttausend bis einige Millionen Prozesse,
Zyklen letztendlich braucht,
bis man da also an die Daten rankommt.
Gut, in der Regel,
damit hatte ich dann eigentlich so ein bisschen aufgehört,
wird eben quasi die Speicherhierarchie automatisch verwaltet,
das heißt also als Programmierer hat man eigentlich damit jetzt nichts weiter zu tun,
nicht weil also im Prinzip das,
was so mit den Cashes passiert,
funktioniert eben automatisch durch die Hardware
Das, was haben wir jetzt insgesamt mit dem Speicherbereich passiert,
also das,
was sich also heute dann abschließend noch als virtuellen Speicher vorstellen werde,
ist eben etwas, was das Betriebssystem macht, das eben dafür sorgt,
dass für mich als Programmierer quasi erstmal der Speicher sehr groß ist,
auch wenn der jetzt vielleicht gar nicht physikalisch in dem Rechner entsprechend verbaut ist.
Das, was ich selber jetzt eigentlich nur aktiv irgendwie beeinflusse,
ist halt eben die Ein- und Ausgabe über Dateien,
wo ich da dann also letztendlich irgendwie was mache
Und wie gesagt, Cash ist dann der schnelle Zwischenspeicher,
der eben dafür sorgt, dass im Prinzip diese Geschwindigkeitslücke,
die sich also quasi zwischen dem Zugriff auf Hauptspeicher,
also DRAM und dem, was also hier quasi Prozessortakt ist,
die dann eben dafür sorgt,
dass also diese Lücke quasi entsprechend klein ist,
beziehungsweise dass man wirklich jetzt also die Daten aus dem Speicher in die CPU bekommt.
In der Regel, wie gesagt,
durch passiert das automatisch,
ist also für uns
quasi transparent
Gut, das war jetzt die letzte Folie,
wo ich ja sammelts schon mit aufgehört hatte,
wo ich eben gesagt hatte,
wenn aus dem Programm heraus irgendwie ein Speichelzugriff passiert,
wird eben gleichzeitig in allen Cashes geguckt und im Hauptspeicher geguckt und irgendwann sagt dann also die Hardware,
also okay, da habe ich was gefunden,
beziehungsweise wenn die hier jetzt also nichts sagen,
dann kriege ich das Betriebssystem dann eben noch mit,
dass also quasi im Hauptspeicher nachgesehen würden,
dass es dann eben entsprechend lange dauern würde.
Bei den Cashes selber wird dann unterschieden zwischen gemeinsamen Cash,
also Unified Cash, sprich Eigentlich entsprechend diesem Fonn-Neumann-Modell,
gemeinsamer Speicher für Instruktionen und für Daten.
Auch da ist es dann eben so,
dass letztendlich in diesem Cash sowohl Instruktionen als auch Daten aus dem Hauptspeicher
dann quasi darüber dann an die eigentliche CPU weitergeleitet werden,
im Gegensatz dazu gibt es dann separate Instruktions- und Datencashes.
Typischerweise, wenn man sich jetzt also wirklich Hardware-Aktekturen ansieht,
sieht das eben so aus,
dass man diese Situation eigentlich bei den First-Level-Cashes hat,
weil Und das war ja das,
was ich also letzte Woche noch vorgestellt hatte,
wenn man Pipelining machen möchte,
um quasi in einem Schritt sowohl auf den Datenspeicher drauf zuzugreifen,
als auch auf den Instruktionsspeicher drauf zuzugreifen.
Also sprich, um jetzt eine Instruktion zu laden in der Zenstruktionsregister,
geht man da dann eben von einer Arbeitarchitektur aus,
wo ich dann eben wirklich getrennte Speicher habe.
Und da spiegelt sich dann eben in der Regel dadurch wieder,
dass man sagt, direkt an der CPU halt eben hier Instruktions- und Datencashes.
Und dann gibt es dann eben jetzt noch weitere Hierarchie-Ebenen von Caters,
das ist bei aktuellen Prozessoren,
ist man dann eben First-Levels hängt, Level, Third-Level-Cash.
Und dann kommt erst der Hauptspeicher hier.
Was sah dann auch noch so ein bisschen mit reinspielt,
ist dann eigentlich die Art und Weise, wo der Cash,
dann nehme ich auch noch physikalisch angeordnet ist.
Und das sind dann nämlich auch die Unterschiede zwischen First-Level und
den anderen Cashes dann auch mit, dass man sagt,
In der CPU oder in dem Programm habe ich jetzt halt meine Adressen oder alles in einem bestimmten Adressbereich.
Da hat er ja schon gesagt, dass es rein praktisch dann immer so aussieht,
dass natürlich jetzt quasi jedes Programm sagt, also mein Adressbereich geht von null bis irgendwas.
Dadurch, dass jetzt aber mehrere Programme gleichzeitig laufen,
passiert dann halt eben noch eine Übersetzung zwischen den virtuellen Adressen,
nämlich das,
womit also hier in der CPU gerechnet wird und den physikalischen Adressen,
das,
womit also im Prinzip auf den Hauptspeicher
drauf zugegriffen wird
So diese Übersetzung, das ist also das,
womit ich dann also heute ein bisschen abschließen werde,
ist dann im Prinzip so ein mehrstufiger Übersetzungsmechanismus,
der dann eben aus sogenannten virtuellen Abreißen physikalische Adressen macht.
Und da gibt es dann eben genau die Sache,
dass man sagt, also ich habe einmal quasi das,
was dann so First-Level-Cash ist, hier vorne,
was dann also im Prinzip mit virtuellen Adressen arbeitet, dann gibt es das,
was hier dann auch als Adress bzw. Datenspeicher gemeint ist,
ein Mechanismus, der dann eben unter anderem dann eben auch diese Adressübersetzung macht.
Nicht nur dann kommen, Halt hier, hinten,
also Cashes, die dann auch schon mit physikalischen Adressen arbeiten,
im Gegensatz zu denen hier.
Sieht man dann eigentlich noch ein bisschen genauer.
Also auf dem hier, Virtual La Cash heißt eben,
ich arbeite jetzt wirklich mit diesen virtuellen Adressen.
Vorteil ist, es muss keine Adressumrechnung erfolgen.
MMU steht übrigens als Akronymer für Memory Management Unit.
Das ist also im Prinzip der Mechanismus in den CPUs,
der dafür sorgt, dass diese Adressumrechnung erfolgt.
Wie gesagt, das muss dort nicht passieren
Nachteil bei dem Ganzen ist, wenn ich einen Kontextwechsel habe,
das heißt, das eine Programm beendet wird,
ein anderer Task irgendwie auf der CPU laufen soll,
dann muss dieser Cash wirklich gelehrt werden, weil das,
was ich ja genau nicht haben möchte,
aus Sicherheitsgründen ist, dass ich sage,
Programmkontext wird unterbrochen,
irgendwas anderes startet und der Prozess guckt dann mal eben noch schnell im Cash nach,
also ob jetzt vielleicht vorher gerade irgendwie der Passwortmanager gelaufen ist und also ob dann im Cash,
also irgendwie in einem Datenbereich dann eben irgendwie die Passwörter des
vorherigen Prozesses rumliegen
Deswegen bei Kontext wechseln muss, sah dann eben entsprechender Cash gelehrt werden.
Beim physikalischen Cash sieht das dann eben so aus,
dass sie dann quasi auf der anderen Seite mit diesen umgerechneten Adressen,
also spricht dann mit diesen physikalischen Adressen arbeitet, dadurch,
dass die dann letztendlich eindeutig gemacht worden sind,
hat man eben dieses Sicherheitsproblem nicht, der Cash muss nicht gelehrt werden.
Auf der anderen Seite habe ich dann natürlich diesen Zwischenschritt mit der Adressumrechnung mit drin,
was dann eben dazu führt, Da ist also der Zugriff 4 drauf,
dann eben auch wieder ein bisschen länger dauert.
Rein praktisch sieht das bei uns jetzt bei den CPUs eben typischerweise so aus,
dass man quasi First-Level-Cash dann eben auf dieser Ebene hat,
Second-Level-Cash dann quasi auf dieser Ebene und das,
was also dann so als Third-Level-Cash bei den aktuellen CBU-Architekturen mit drin ist,
ist dann eben nochmal wieder ein Übergeordneter Zwischenspeicher,
der dann in dem Fall nämlich genommen wird,
um dann quasi von allen CPUs,
also sprich,
wenn ich einen Prozessor mit 4 oder mit 8 Kernen habe,
von allen CPUs dann nochmal einen gemeinsamen Cash zu haben,
der ansonsten sowohl First-Level als auch Second-Level-Cash
sind dann eben immer exklusiv für jede CPU
einzeln verfügbar
Gut, das ist also im Prinzip hier also auch nochmal kurz aufgelistet. Und ja,
was dann letztendlich das Problem bei dem Ganzen immer ist,
wenn ich jetzt eben wirklich mehrere Prozesse Kerne habe,
diese Datenkonsistenz zu halten,
wofür man dann eben im Prinzip so verschiedene Protokolle braucht,
die einem das dann abnehmen.
Hatte ich also auch schon ganz kurz angedeutet, was dahinter ist,
werde ich also auch gleich nochmal an einem kurzen Beispiel beschreiben.
Ansonsten kann man sich bei diesem Caching dann immer noch so
ein paar unterschiedliche Strategien überlegen
Grundsätzlich kann man immer erst mal fragen, was soll in
den Cash? Da muss man jetzt sagen, okay,
also gibt es dann also hier irgendwo in der CPU also die große Glaskugel,
die sagt, in den Cash soll das,
was also in der Zukunft jetzt möglichst bald benötigt wird.
Weil dann funktioniert das Ganze optimal, weil dann muss ich nicht
lange bei irgendwelchen Adress zugriffen auf Instruktionen oder auf Daten,
auf den langsameren Hauptspeicher warten, sondern ich finde die Daten also quasi direkt in den Cash. So,
und da würden im Prinzip dann also zwei Mechanismen einmal eine Temporale oder zeitliche Lokalität,
wo man also im Prinzip davon ausgeht,
wenn ich jetzt also irgendwie ein Programm habe,
was jetzt also auf irgendwelchen Adressen seinem Programmcode hat oder was eben unter bestimmten Adressen seine Daten hat,
dass man dann eben sagt, also das,
was quasi jetzt in der Vergangenheit gerade benötigt wurde,
wird, höchstwahrscheinlich in der Zukunft auch wieder benötigt,
beziehungsweise ein bestimmter Bereich drumherum, dass man eben sagen kann, ich habe da jetzt also quasi eine gewisse Lokalität.
Räumlich in dem Sinne, dass ich dann eben sage,
hinsichtlich der Adressen bleibe ich in einem bestimmten Bereich,
bei den Instruktionen ist das klar,
dass wenn ich jetzt nicht gerade irgendwelche Unterprogrammaufrufe habe oder irgendwelche Riesensprünge in meinem Programm,
dann werden die Assembler-Instruktionen ja quasi sukzessive irgendwie abgearbeitet und wenn ich eben in einem bestimmten Bereich bin,
dann kann ich sagen, okay,
wenn ich jetzt nicht gerade wirklich irgendwas ganz anderes anspringe,
dann bleibe ich eben von den Adressen irgendwo in diesem Adressbereich und sage,
dann wird eben die Im Idealfall nächste, übernächste,
überübernächste Anweisung dann eben entsprechend gelesen.
Bei den Daten gilt genau das Gleiche. Dazu hatte ich also
am Ende dieses Kapitels zur Assembler-Programmierung so ein bisschen gezeigt,
wie im Prinzip die Daten in den Hauptspeicher gelegt werden.
Auch da kann man sagen,
dass wenn ich jetzt also innerhalb eines Programmkontextes bin,
also sprich innerhalb einer Prozedur, dort lokal irgendwelche Datentypen deklariert habe,
dann liegen die natürlich also irgendwo an benachbarten Adressen im Hauptspeicher.
Nicht das wäre dann eben letztendlich dann das,
was hier mit räumlicher Lokalität gemeint ist
Gut, dann gibt es eben verschiedene Ersetzungsplatzierungs- und Schreibstrategien.
Dazu erst nochmal ein paar Begriffe. Wenn man jetzt also
sich anguckt, also wie effizient ist so ein Cash?
Also ich habe einen Treffer, wenn ich auf ein Datum zugreife,
das bereits im Cash ist. Ich habe einen Fehler oder
einen Miss, wenn das Datum nicht im Cash ist.
Das kann man dann quasi auch wieder irgendwie statistisch erfassen.
Das heißt, ich habe irgendeine Wahrscheinlichkeit zwischen 0 und 1 für die Trefferrate bzw. für die Fehlerrate.
Und ich kann letztendlich ausrechnen, wie lange Dauert der Datenzugriff,
wenn ich jetzt im Falle eines Sit irgendwas aus dem Cash lese,
wie lange dauert der Datenzugriff, wenn ich jetzt eben quasi einen Miss habe.
Das heißt, die zusätzliche Zeit, die benötigt wird,
um die Daten aus dem Hauptspeicher in den Cash zu holen und dann von dort aus zu lesen.
Denn dann kann man einfach jetzt mit diesen Werten,
die man ja so irgendwo dann quasi per Monitoring irgendwie statistisch ermitteln kann,
so was wie mittlere Zugriffsspeicher oder Speicherzugriffszeiten ausrechnen,
wo ich dann eben sage, okay,
ist halt einfach die Zeit, die ich für Ethabe plus der Wahrscheinlichkeit,
dass ich Miss habe und der entsprechenden Zugriffszeit.
Das heißt, als Beispiel, wenn ich jetzt eben sage,
im Falle, ich finde irgendwas im Cash,
kriege ich mit einem Takt ausgelesen. Nicht,
dass wir ja dann entsprechend hier die Zeit im Falle eines Cash Misses,
dauert es dann 20 Takte, bis sich das Datum also quasi aus dem Hauptspeicher in den Cash transferiert habe.
Und ich habe eben Missrate von 5 Prozent, Nicht,
dass einfach in die Formel eingesetzt,
kriegt man dann also sowas wie eine mittlere Speicherzugriffszeit von zwei Takten.
Gut, und das, was man eigentlich dann machen möchte,
ist letztendlich hier an diesem Therm drehen und eben entweder die Wahrscheinlichkeit für einen Fehler,
was noch weiter runter zu drehen,
beziehungsweise da dann eben die Geschwindigkeit noch aufzudrehen.
Gut, wie ist der Cash letztendlich organisiert oder wie muss man sich das vorstellen?
Also es ist im Prinzip auch ein Reh von Speicherbereichen,
die enttalen dann immer quasi Cash-Zeilen. Das ist also das,
was dann also hier letztendlich immer angedeutet ist. Und in den Zeilen nehmen Verwaltungsinformationen,
gibt es dann eben die entsprechenden Datenblöcke, die dann aber,
sagen wir mal jetzt anders als der Hauptspeicher nicht beidweise adressiert werden,
sondern dann also wirklich in entsprechend deutlich größeren Einheiten.
Nicht das heißt,
also ich habe dann hier immer gleich im Bereich von einigen,
sich beid bis zu ihm irgendwie Kilobyte, je nach Größe,
wo man dann eben entsprechend sagen kann,
dass ich hier dann eben entsprechend jetzt im ganzen Satz aufeinander folgender Speicherworte in einem Cash mit drin habe.
Gut, wie sieht das rein praktisch aus?
Also ich habe jetzt irgendwie meine Adresse. Erstmal unabhängig davon,
ob das jetzt eine physikalische oder eine virtuelle Adresse ist.
Also ich habe hier irgendwie meine Adresse von Mbit. Und das,
was man macht, ist,
dass man dann im Prinzip mit einem Teil der Bit
quasi jetzt also hier diesen oder Cash-Bereich, also das,
was sie als Set bezeichnet ist, adressiert.
Also 0,1 bis S minus 1. Nicht, das wird eben
entsprechend dann jetzt hier über diese Anbits für diesen Set-Index adressiert.
Dann habe ich ein Blog-of-Set,
damit referenziere ich jetzt innerhalb dieser Cash-Zeile ab welcher Stelle
ich jetzt für einen bestimmten Bereich meine Daten lesen möchte
Und der Rest der Adresse, das ist dann das sogenannte Tech.
Das ist nämlich das, was jetzt quasi beim Cash noch nicht weiter berücksichtigt wurde.
Das wird dann einfach als Zusatzinformation in diese Cash-Zeile mit reingeschrieben.
So, wie sieht dann der Speicherzugriff selber aus? Dazu gehe ich jetzt,
also hier, oder springe ich hier noch mal kurz,
kurz über den einen Teil,
wo man das jetzt also im Prinzip so noch mal kurz angezeigt ist,
wie das dann also aussieht.
Also ich adressiere jetzt hier einmal Mindset,
beispielsweise jetzt eins, weil es jetzt also entsprechend,
ich bin jetzt in diesem Set eins, wenn ich das gemacht habe,
gucke ich also im Cash jetzt oder wird jetzt in einem Cash quasi dieser Set eins ausgelesen.
Da muss jetzt also ein Valid-Bit gesetzt sein,
also sprich erstmal gesagt sein, dass es überhaupt gültige Informationen ist,
wann es Es ist keine gültige Information nach dem Kontextwechsel oder wenn
der Rechner eingeschaltet wurde und es wurde noch gar nichts in den Cash geladen.
Nicht, weil physikalisch stehen dort natürlich irgendwie Nullen und Einzeln drin,
aber das sind in dem Sinne jetzt keine gültigen Worte oder Werte.
So und was man dann eben macht, um jetzt zu gucken,
ob das jetzt überhaupt eine gültige Adresse war,
dass man dann anschließend eben diesen Tech überprüft,
also sprich diesen Rest der Adresse,
ob der dann identisch ist mit dieser Adresse,
wenn er nicht identisch ist mit der Adresse,
dann ist dort quasi irgendein anderes Datum gecashed worden und ich habe ein Cash-Mace.
Wenn da identisch ist, dann kann ich eben sagen,
meine Adresse referenziert hier einmal eben auf diesem Set.
Der Tech stimmt überein und dann kann man mit dem Blog
auf Set dann eben quasi innerhalb von diesem Bereich sagen,
ab welcher Stelle ich jetzt eben hier meine Bytes,
also in diesem Fall beispielsweise jetzt also irgendwie hier vier Bytes oder vier Worte gelesen werden sollen.
Also das ist erstmal grob gesagt die Art und Weise,
wenn man jetzt also auf den Cash
drauf zugreift
Gut, wenn man jetzt sagt, wie,
oder was kann dort alles noch weiter passieren,
dann wird man immer unterscheiden zwischen, also hinsichtlich der Cash-Organisation,
ob man sogenannten Direktabgebildeten Cash, also Direct-Map im Englischen, Endfachbereichsassoziativen Cash hat.
Wenn man also in die Datenblätter guckt, bei den Prozessorherstellern,
dann sagen die teilweise, also irgendwie Cash zweifach assoziativ oder vierfach assoziativ,
das ist eben genau dieses, hier werde ich gleich erklären,
was sich dahinter verbirgt.
Oder es gibt dann eben auch sogenannte Fallassoziative Cashes,
sind teilweise in den CPUs auch mit drin für bestimmte andere Übersetzungsmechanismen.
So, der einfachste Art und Weise,
diese Adressabbildung zu machen, ist eben der direkt abgebildete Cash.
Wie muss man sich das vorstellen? Ich habe jetzt hier meinen Speicherbereich mit irgendwelchen Speicheradressen. So,
und das, was man jetzt hier macht,
um den jetzt also quasi auch jetzt hier angenommen jetzt diese acht Cash-Zahlen irgendwie abzubilden,
ist, dass man dann ja eben sagt, okay,
ein Teil der Adresse, nämlich und ordnet dem der Cash-Zeile zu.
In diesem Fall sind das also hier quasi die unteren drei Beet. Das heißt,
die Adresse 0-0, 0-0-1,
die Adresse 0-1, 0-0-1,
die Adresse 1-0,
0-0-1 und die Adresse 1-1,
0-0-1, zeigen quasi alle auf die gleiche Cash-Adresse.
Da sieht man dann eben Unterschied jetzt quasi zwischen dem Ähm,
Set-Index und dem Tech, das ist nämlich genau das,
dass man sagt, also quasi dieser Set-Index wären hier die
unteren drei Bit und der Tech ist dann eben der verbleibende Teil der Adresse,
der nicht berücksichtigt wurde, in diesem Fall ist die oberen Zweibelt.
Das heißt, wenn ich einen Zugriff auf diese Adresse mache,
ist klar, mit dem Dreibit, sage ich genau,
also auf diese Cash-Zeile,
aus dieser Cash-Zeile lese ich dann quasi den dort vorhandenen Tech
Das sind dann eben die noch fehlenden Bits. Und wenn dort 0-0 steht,
wäre das dann quasi dieser Zugriff und ich kann hiermit weiter arbeiten,
wenn dort also irgendwie 0-1,
1-0 oder 1-1 steht,
dann ist hier quasi einer von diesen anderen Werten im Cash gespeichert gewesen.
Das ist also die Sache, wofür man also letztendlich diesen Tech braucht,
das ist dann eben der verbleibende Tal der Adresse,
um dann quasi zwischen den Einzelnen Elementen zu unterscheiden,
die dann eben entsprechend darauf zugreifen. Gut,
beim direkt abgebildeten Cash gibt es eben quasi pro Bereich,
also pro Set genau eine einzige Zeile,
wo ich dann eben letztendlich sage,
Mit einem Teil der Adresse bilde ich dann also dieses ab,
habe dann eben hier unten meinen Blog-Index,
um dann hier reinzugehen und habe dann eben letztendlich die verbleibenden Arten und Weisen hier oben als Tag.
Ist eben vergleichsweise direkt und einfach zu implementieren. Damit kann man also auch
sehr große Cashes bauen. Was man hier natürlich dann sofort sieht,
ist, wie man also quasi diesen ganzen Cashing-Mechanismus torpedieren kann,
beziehungsweise das Ganze nennt sich dann immer irgendwie Cash-Frashing,
wenn ich Zugriffe habe,
die quasi jetzt immer Vielfache von den Bereichen hier und der Anzahl der Sets haben.
Das heißt, wenn ich also erst auf dieses Element drauf zugreife,
was passiert, es ist entweder im Cash oder es muss eben in den Cash geladen werden.
Und wenn ich jetzt quasi mit dem nächsten Zugriff in meinen Datenstrukturen auf diese Adresse drauf zugreife,
dann weiß ich genau, okay, zeigt eigentlich auf den gleichen Cash-Set,
aber da habe ich dann jetzt also quasi das falsche Element drin,
dem zufolge hundertprozentig ein Cash mehr ist.
Das ist dann nämlich die Art und Weise,
wie man abhängig von der Größe des Cashes und eben diesen Arten und Weisen,
wie man jetzt also irgendwie Adressen berechnet, dafür sorgen kann,
dass das Ganze eben besonders gut oder besonders ineffizient läuft.
Also dadurch, dass sich dann also quasi diese Adressen hier
immer genau eins zu eins überlagern. Gut, Zugriffsmechanismus hatte
ich hier also schon kurz skizziert, wie das geht.
Also wie gesagt, entsprechend die Auswahl. Und wenn das
dann eben entsprechend klar ist, dann kann ich dann eben drauf zugreifen.
Rein technisch ist das dann also auch sehr einfach zu realisieren,
nämlich dass man sagt,
okay, ich nehme hier quasi meine Adresse,
sage also ein Teil der Adresse, Nämlich das,
was also quasi unten dann, also dieser Blog-Index ist,
steuert nichts anderes als hier ein Multiplexer,
wo ich dann also quasi aus dieser Cash-Zeile eben die entsprechenden Bereiche mir ausblenden kann.
Der andere Teil der Adresse adressiert dann quasi über ein Dekoda,
die entsprechende Cash-Zeile, also das,
was dieses hier ist und der Rest der Adresse muss dann quasi mit dem Tech,
was im Cash drin ist, verglichen werden.
Und nur wenn das der Fall ist, habe ich einen Hit.
Und wenn nicht, müsste ich dann eben quasi das Datum aus
dem Hauptspeicher laden und dann eben unter dieser Cash-Adresse dann also einspeichern.
Das heißt also, man hat hier nichts anderes als ein ganz normal adressiertes Rahmen.
Ich habe hier einen Vergleicher und ich habe hier einen Multiplexer,
ist also wirklich sehr einfach, entsprechend eins zu einzelne Hardware umzusetzen.
Gut, hier folgt jetzt also nochmal so ein bisschen so ein kleines Beispiel,
wo man das also mal in der Praxis sehen könnte.
Also da kann man das nochmal so ein bisschen nachvollziehen.
Ich werde das jetzt also hier nochmal Ganz kurz skizzieren.
Also wie gesagt, direkt abgebildeter Cash zurzeit ist hier also alles leer.
Also Wellett ist nicht gesetzt, Text sind leer,
Daten sind hier irgendwie auch mit irgendeinem Bitmuster gefüllt.
Acht Register, Programmzähler, Instruktionsregister im Hauptspeicher habe ich hier
irgendwo in einem Speichelbereich meine Instruktion und hier also irgendwelche Daten.
So, was passiert? Programmzähler zeigt also letztendlich,
also auf diese Adresse. Es soll also irgendwie so
ein Lotbefehl
ausgeführt werden
Unter dieser Adresse wird geguckt. Ich habe ein Cashmit,
weil zurzeit hier noch alles ungültig ist und die Adresse nicht da sind.
Also was muss passieren? Von dieser Adresse wird aus
dem Hauptspeicher gelesen und dann eben ein ganzer Bereich,
also sprich alles, was hier so ein bisschen bräunlich eingefärbt ist.
Letztendlich dann in den Cash reingeladen. So,
wenn das passiert ist, dann wird eben von entsprechender Adresse
dann eben dieser Befehl gelesen, landet im Instruktionsregister,
würde dann dort dekodiert werden
So, was ist das jetzt für ein Befehl, wenn der Execute-Phase,
also Adresse von der A1 mit dem Offset von 8,
wäre also 1-0, 0, 0,
4, 8. Anfrage passiert an den Cash.
Dort wird festgestellt, das ist nicht im Cash, es gibt wieder einen entsprechenden Cashmith.
Der ganze Bereich wird quasi jetzt aus dem Hauptspeicher,
also hier jetzt irgendwie an die Sadresch,
in den Cash geladen und ich, und wenn das der Fall ist,
kann dann eben das entsprechende Datum in Register R2 gelesen werden.
Gut, im nächsten Schritt beim nächsten Befehl würde jetzt also hier quasi die Sload R3 ausgeführt werden,
beziehungsweise würde erstmal quasi ins Instruktionsgerichtsregister geladen werden,
Programmfehler wurde inkrementiert, dieser Befehl soll gelesen werden und Hurra,
den finden wir jetzt also,
nachdem das das erste Mal quasi hier geladen wurde aus dem Cash,
kann also direkt ins Instruktionsregister geladen werden
Dort wird der Befehl dann quasi ausgeführt. Auch da
habe ich jetzt wieder einen Cash-Shit und das entsprechende Datum
würde dann eben in Register 3 geladen werden.
So, beim nächsten Befehlen Additionsbefehl,
auch der findet sich dann also schon direkt im Cash,
wird dann also quasi ins Instruktionsregister geladen, während der Execute-Phase.
Gut, da habe ich sowieso keinen Sparicherzugriff,
sondern der rechnet letztendlich hier nur mit den beiden Registern
R2 und R3 rum.
Die Summe landet dann also in
Und ja, dann würde eben entsprechend hier entsprechend weitergearbeitet werden. So,
wie gesagt, das kann man jetzt ja noch entsprechend weiter durchspielen,
was dann also hier so auf den nächsten Folie noch kommt.
Und das zeigt dann aber im Prinzip, also wirklich immer so rein praktisch,
wie dann also die einzelnen Speicherbereiche sukzessive in den Cash geladen werden,
beziehungsweise dann von dort aus was gelesen wird.
Gut, das ist also erstmal so von der Idee her
die Art und Weise, wie man sich das vorstellen muss,
wie ja so im Prinzip so ein direkt abgebildeter Cash arbeitet.
So bei den sogenannten Enfanten, einfach assoziativen Cashes,
sieht das eben so aus, dass ein Set jetzt eben nicht nur ein,
sondern zwei oder vier mögliche Speicher, Zeilen hat,
wo also quasi jetzt Werte gespeichert werden können.
Und das, was dann dort einfach passiert, ist,
dass man jetzt sagt, eigentlich genau wie beim Direkt abgebildeten Cash. Ich habe hier meinen Set-Index,
indiziere da jetzt quasi in den Set hinein,
weiß jetzt eben, dass es dieser Bereich,
in der jetzt aber in diesem Fall jetzt also quasi zwei Cash-Zahlen beinhaltet und anschließend muss man quasi jetzt sagen,
das gültige Wort kann jetzt eben,
wenn es überhaupt im Cash ist, nicht nur an einer,
sondern an zwei oder wenn das ein Vierfach-Assoziativer ist,
quasi an vier möglichen Stellen sitzen.
Was muss dabei passieren? Eigentlich genau wie bei dem anderen,
also dieses Wellet-Bit muss gültig sein und es wird eben verglichen,
ob das Tag entweder diesem Tag oder diesem Tech,
beziehungsweise wenn das vierfach assoziativ wäre,
müsste ich das dann eben quasi insgesamt mit vier möglichen Techs vergleichen.
Wenn ich das hier drin finde und wähle es gesetzt ist,
dann kann man eben über den Block-Off-Set dann eben auch wieder
hier entsprechend die Daten rauslesen.
Also das ist im Prinzip die Art und Weise,
wie jetzt so ein mehrfach assoziativer Cash arbeitet.
Rein technisch heißt das dann aber eben,
dass da natürlich ein bisschen aufwendiger ist.
Sprich, ich habe dann also entsprechend hier jetzt einfach
zwei Rams
Die dann also quasi parallel adressiert werden. Ich habe dann eben hier
quasi zwei Vergleiche, die parallel stattfinden und eben einen Hit,
wenn entweder der wahr ist oder der wahr ist und dann eben auch hier diese entsprechenden Auswahlen.
So, das, was man da dann einfach sagen kann,
verglichen mit direkt abgebildeten Cash ist,
der dann halt doppelt so aufwendig, wenn das ein Vierfach-Assoziativer wäre,
dann wäre er also quasi viermal so aufwendig.
Andererseits habe ich hier natürlich eine größere Wahrscheinlichkeit,
also jetzt irgendwie einen Hit zu landen,
weil ich hier quasi unter dieser Adresse nicht nur einmal,
sondern zweimal oder viermal
irgendwelche Bereiche abgespeichert haben kann
Und das ist dann eben etwas, was dann so ein bisschen immer
der Offset ist, wie man die Cashes jetzt umbaut. Ja,
vollassoziativer Cash, kann man sich dem zuvor genannt schon denken,
dass es dann einfach ein sehr kleiner Cash,
wo wirklich an beliebiger Stelle irgendwo das Datum gespeichert werden kann,
wo ich dann aber eben auch entsprechend mit allen anderen Einträgen diese Text vergleichen müsste.
Gut, wie werden die Cashes dimensioniert? Also zum einen unterscheiden die sich jetzt natürlich,
also ob das jetzt einfach oder mehrfach assoziativ ist und dann eben letztendlich,
wie groß jetzt hier diese Bereiche sind für Blog-Off-Set,
für die Anzahl der Sets und für das, was dann eben noch an Text-Bits letztendlich überbleibt.
Da kann man dann eben sagen, das ist dann immer so ein bisschen davon abhängig, wie jetzt räumliche und zeitliche Lokalität aussieht. Nicht,
weil, was ja eben dieses Beispiel auch schon so ein bisschen gezeigt hat,
wenn ich jetzt also in einer normalen Programmabarbeitung bin und sage,
ich habe jetzt also der Reihe nach meiner Instruktion,
dann kann man natürlich sagen, im Bereich des Instruction Cash,
wenn die jetzt erstmal da rein nach ausgeführt werden,
hat man natürlich relativ häufig, wenn das Ganze in den Cash geladen wurde,
also irgendwo ein Cash hit, beziehungsweise was in diesem Beispiel dann ja auch war, dass das Ganze dann eben auch für die Adressen letztendlich gilt.
Das heißt, da nutzt man dann eben diese Lokalität aus,
was dann auch rein praktisch passiert bei der Übertragung von Hauptspeicher in den Cash hinein,
ist, dass letztendlich Ich hatte ja schon gesagt,
also hier sind wir jetzt nicht nur einzelne Bytes,
sondern wirklich größere Bereiche in so einer Cashline mitenthalten,
dass man dort dann eben auch entsprechend deutlich größere Bereiche aus dem Hauptspeicher überträgt.
Zum einen macht man sich da zunutze, dass also die Wortbreiten,
wenn ich jetzt also im Prinzip hier sowas nehme,
wie jetzt also in meinem Desktop-PC mit den Speicherriegeln mal angucke,
nicht und sagen, na gut,
der hat also doch ein paar mehr Pins als jetzt irgendwie nur einige für die Adresse und einige für die Daten,
dass eben letztendlich die Wortbreiten, auf die dann zugegriffen wird,
dann eben auch gleich Im Bereich von 100 Bits beziehungsweise noch mehr ist,
also das heißt jetzt eben nicht nur Einbart oder Zweibyte oder sowas,
sondern also doch entsprechend breitere Bereiche.
Und was man da dann eben auch ausnutzt, sind dann sogenannte Burst-Adressierungsmodi beim Speicher,
dass man im Prinzip dem Hauptspeicher sagt, starte bei der Adresse zu lesen
Und anschließend kommen dann einfach in aufeinanderfolgenden Takten immer aufeinanderfolgende Speicherbereiche und die Adressen werden automatisch raufgezählt. Das heißt,
man muss jetzt nicht jedes Mal sagen,
von der Adresse lesen und dann kriege ich das Datum und von der Adresse lesen und dann kriege ich das Datum,
sondern man startet dann eben so eine Birst-Übertragung und kann dann eben sagen,
ab der Adresse und dann kommen Daten, Daten,
Daten und damit kriegt man natürlich relativ gut,
gerade wenn das dann eben auch entsprechend größere Wortbreiten sind,
also quasi diesen Cash oder so ganzen Cash-Bereiche entsprechend aufgefüllt
Nicht, das ist also das, was dann also rein
technisch dort dann auch so ein bisschen mit dem Persi.
Was man sich dann auch immer noch eben überlegt,
ja so hinsichtlich diese Dimensionierung, wie welche Vorteile hat es,
wenn ich jetzt große Cashblöcke mache oder wenn ich kleinere Cashblöcke mache.
Wenn ich die Cashblöcke größer mache, habe ich natürlich den Vorteil,
dass ich quasi jetzt die räumliche Lokalität besser ausnutze.
Das heißt, aufeinanderfolgende Daten Ich kann also beispielsweise irgendeine Rehe,
kann ich sagen, wenn ich also irgendwie also auf den ersten Index
darauf zugegriffen habe, kann ich mir relativ sicher sein.
Der zweite, dritte und vierte und wie viele auch immer
sind erstmal schon beim nächsten Zugriff automatisch im Cash mit drin.
Nachteil bei großen Blockgrößen ist, dass die Misspanalty groß ist,
weil wenn ich jetzt also quasi einen großen Cash-Block habe und ich habe einen Cash-Miss,
heißt das, ich muss erstmal sehr viele Daten aus dem Hauptspeicher holen,
um diesen Cash zu füllen.
Das dauert natürlich deutlich länger, als wenn ich jetzt wieder
kleinere Blöcke habe. Das ist also das,
was hiermit angedeutet ist, dass quasi steigender Blog,
sei es dann eben diese Misspanel, die auch quasi linear raufgeht.
So hinsichtigt der Fehlerrate, kann man eben sagen,
wenn ich die Bloggröße kleiner mache und die also zu klein ist als das,
was ich jetzt quasi in meinem Datenbereich irgendwie adressiere.
Das heißt, ich habe im Cash weniger Daten als das,
was ich jetzt in meinem Programm gerade an Variablen brauche,
führt das automatisch dazu, dass ich also irgendwo im Programm laufe,
dann irgendwann mal Cashmisses habe.
Das heißt, ich sollte die Bloggröße nicht zu klein machen,
weil dann habe ich eben Probleme mit der räumlichen Lokalität war ich
also quasi jetzt meinen gesamten Adressen, nicht meine Cash reinbringen kann.
Wenn ich die Blockgröße aber wieder zu groß mache,
kriegt man also Probleme mit der zeitlichen Lokalität,
war ich dann also dort wieder zu viele Daten habe.
So wenn man das jetzt alles zusammennimmt, kann man sagen,
es gibt halt typischerweise sowas, wo es irgendwo so eine Art Optimum gibt, hinsichtlich der Größe.
Gut, glücklicherweise Haben sich Leute damit also irgendwie auseinandergesetzt,
die sich mit sowas auskennen, die das also über zig Jahre analysiert haben,
mit allen möglichen Programmen gebenchmarkt und ausprobiert haben. Also von daher kann man sagen,
also da wir jetzt also nicht in der Lage oder in der Regel erstmal nicht vor der Entscheidung stehen,
wir bauen uns unseren Prozessor selber,
wo ich dann nämlich solche Entscheidungen treffen müsste, kann man sagen,
okay, das ist also irgendwie gottgegeben von Firma Arm,
Intel, AMD oder wer auch immer asymmiert, oder nicht,
also wer auch gerade immer mir jetzt also hier quasi meine,
meine CPU aus irgendeinem Gerät oder irgendeinem Smartphone passend dann also gebaut hat.
Also was man hier auch nur sieht,
ist dann eben wirklich mal, also sowas mit Benchmarkdaten,
also irgendwie ausgemessen, wo man dann eben sagen kann,
also zum einen habe ich hier sowas wie diese Blockgröße,
dann eben hier quasi das,
was ich als Missrate daraus ergibt und dann also hier quasi die Größe der Cashes,
nicht von 1K bis 256K,
wo man dann eben sagen kann, okay,
Je größer ich die Cashes mache,
umso kleiner werden hier also erst mal quasi diese Missraten.
Und was man aber eben teilweise dann schon sieht,
es gibt dann eben irgendwelche lokalen Minima oder so in diesem Bereich.
Und typischerweise kann man sagen, das Wasser sind den Prozessoren,
die eben so verbaut ist,
dass also irgendwie so Blockgröße zwischen 32 und 128 Byte und
die Cashes sind dann eben quasi Level 1 Cash,
so bis 256 Kilobyte,
Level 2 Cash dann also schon bis vier Megabyte und
entsprechend dann eben noch größer werden
Hier sind, machen wir mal so Vergleiche, wie das dann also aussieht,
die Größe und Unterschiede zwischen vollassoziativen Zwahlfachassoziativen direkt abgebildeten Cash.
Da sieht man, okay, also Vorteile habe ich jetzt
also im Prinzip bei diesen mehrfach assoziativen letztendlich nur,
wenn man entsprechend größere Cashgrößen hat,
wo dann also hier quasi die Performance entsprechend deutlich steigt.
Gut, aber wie gesagt, das sind in der Regel Parameter,
die wir nicht selber beeinflussen,
sondern die dann also irgendwo in der Hardware letztendlich vorgegeben ist.
Gut, bei den Cashen ist es ja auch noch mal
kurz beschrieben, gibt es da noch
so Unterschiede
Welche Adlers sind nicht? Also zum einen ist es natürlich,
wenn der Cash noch nicht beschrieben ist,
habe ich automatisch einen Mist, das ist dann das,
was er hier cold mess ist, Konflikt miss,
wenn ich eben quasi was adressiere, es aber nicht im Cash letztendlich landet,
weil quasi irgendein anderes Datum beziehungsweise irgendeine andere Adresse,
was in den Cash geschrieben hat.
Und hier sieht man dann eben auch letztendlich ja immer diese Problematik,
was ich ja so vorhin schon kurz angedeutet hatte,
nicht wenn ich ja sowas wie so einen direkt abgebildeten Cash habe,
Setgröße 8, nicht immer quasi abwechselnd Zugriffe auf die Blöcke 0,
8, 0,
8 mache,
dass ich dann also wirklich mit jedem Zugriff genau
einen Cash misserzeuge
Nicht, was dann eben genau zu diesem immer gegenseitigen Verdrängen führt.
Das ist jetzt natürlich immer kritisch bei direkt abgebildeten Cashes,
bei mehrfach assoziativen, könnte man sagen, okay,
also das wird dann eben beides letztendlich bei so einem Zweifach assoziativen entsprechend abgebildet.
Ja, und Capacitymist ist dann eben letztendlich, wenn der Cash letztendlich für das,
was sich an Daten dort unterbringen möchte, zu klein ist. Gut,
bei der Ersetzung gibt es dann eben auch unterschiedliche Strategien,
also ich kann, die Sache ist,
ich habe jetzt irgendwie meinen Cash gefüllt Habe jetzt also irgendwie ein Mehrfach assoziativen Cash,
habe einen Cash-Mith,
irgendwelches Datum muss jetzt also quasi in den Cash geladen werden.
Jetzt steht man davor, was wird jetzt rausgeschmissen an Datum?
Weil ich habe jetzt in meinem Blog ja,
also auf einmal zwei oder vier Einträge, die dann also gegebenenfalls rausgeschmissen werden können.
Nicht, das kann man rein zufällig machen. Ich kann dann
im Prinzip sowas wie Least Recently Houst machen. Das heißt,
es werden wirklich quasi irgendwelche Informationen mitgeführt,
wann wurde Eine Cash-Zeile beziehungsweise eine Adresse das letzte Mal zugegriffen und die,
die jetzt am seltensten oder in der Vergangenheit jetzt quasi die Älteste ist,
wird dann eben letztendlich herausgenommen.
Und wo man gesagt hat, okay,
da habe ich jetzt also gerade vor einem Takt drauf zugegriffen
oder habe ich vor zwei Takten aufzugegriffen.
Nicht, dann wird die Wahrscheinlichkeit hoch sein,
dass man das demnächst wieder braucht.
Das wird dann eben noch nicht rausgenommen. Oder liest Frequent Leuse,
das heißt, da werden dann sowas wie Zugriffszähler implementiert und
dann eben geguckt,
wie oft wurde auf bestimmte Adresse
Zugegriffen. Was dann auch noch mit reinkommt,
ist dann eben die Art und Weise Schreibstrategie.
Also ich verändere jetzt meine Daten, schreibe jetzt irgendwie die Daten zurück.
Dann würde das ja eigentlich heißen, dass die im Cash
und letztendlich nachher dann also auch irgendwo im Hauptspeicher verändert werden müssen.
So, und da kann man jetzt sagen,
ich mache sowas wie Wright Through, das heißt,
also die Daten werden sowohl im Cash als auch direkt im Hauptspeicher verändert,
führt dazu,
dass sich im Hauptspeicher
immer aktuelle Daten habe
Kostet mich aber natürlich relativ viel Performance,
weil wenn ich jetzt also erstmal lokal auf irgendwie den Daten also nur rumrechne,
das Schreiben auf den Hauptspeicher ist natürlich genauso wie die anderen Zugriffe dann eben auch entsprechend langsamer,
ist es natürlich besser zu sagen, okay,
ich verändere das Datum, wenn ich jetzt gesagt habe,
also der Wert soll jetzt also nicht mehr 5,
sondern 7 sein,
verändere das Datum erstmal nur im Cash,
rechne damit dann vielleicht weiter und wenn das Programm beendet wird oder eben alles letztendlich fertig ist,
dann wird es jetzt in der Hauptspeicher zurückgeschrieben.
Nicht das wäre dann eben Wright Back, sprich,
wenn das Datum aus dem Cash ersetzt werden würde, dann wird das Ganze erst gegeben.
Im Hauptspeicher zurückgeschrieben. Beides hat Vor- und Nachteile, wie gesagt,
bei Wright Thru habe ich immer Konsistenz, konsistente Daten, also diese Cash-Co-Rences gegeben.
Bei Wright Back ist halt von der Performance deutlich besser,
hat aber eben den Nachteil,
dass man eben wenn er jetzt mit mehreren Prozessoren auf dem Speicherbereich arbeitet,
dort dann eben dafür sorgen muss, dass sich dann also konsistente Daten habe,
beziehungsweise was man dann teilweise macht, ist,
also dass die Betriebssysteme dann eben sowas haben wie Speicherbereiche,
die nicht gecashed werden dürfen,
beziehungsweise es gibt dann eben auch spezielle Befehle,
die dafür sorgen, dass der Cash gelehrt werden kann.
Ja, Cash-Kohärenz hatte ich ja schon kurz angesprochen,
nämlich mehrere Prozessoren greifen irgendwie auf die Daten oder gleiche Daten
im Speicher drauf zu,
die liegen aber lokal im Cash
Führt letztendlich dann dazu,
dass die Hersteller da jetzt also sehr viele sehr unterschiedliche Protokolle implementiert haben,
wie man sowas lösen kann, also von einem einfachen Ride Through,
wo ich das Problem gar nicht habe, über verschiedene Varianten,
die, also das Bekannteste, was man immer findet,
ist mesi, quasi jetzt über Status-Bits sagen,
habe ich dort einen modifizierten Wert, ist das ein exklusiver Wert,
das heißt, gehört er mir alleine,
wird er noch mit irgendwelchen anderen Cashes geteilt
Oder ist das ein ungültiger Wert? Nämlich,
das Ganze wird dann immer in Form, also hier springe ich mal kurz drüber,
weil das ist also nicht im Einzelnen, erklären werde,
wird dann immer in Form von solchen Automatendiagrammen gerne beschrieben,
wo man dann sieht, wie da also quasi jetzt die Unterübergänge sind.
Ich werde das jetzt also eher an diesem reinen praktischen erklären,
was dann eben auch ein Beispiel dafür ist,
wie dieses sogenannte Snoobing funktioniert, also mit dem Snoobing,
englisch schnüffeln, geht es darum, dass quasi,
wenn ich jetzt mehrere Prozessoren habe,
also sprich hier mal ein Vierkern CPU,
Im Prinzip jeder Prozessor oder der Prozessor-Kern letztendlich immer guckt,
am Speicherbus,
ob ein anderer Prozessor eben auf diese Adressen drauf zugreift und gegebenenfalls muss ich dann eben darauf reagieren.
Also wie sah es aus? Wir haben hier einen Hauptspeicher
, haben hier also quasi zwei CPUs.
Die CPU 1 lädt jetzt eben ein Wert A von einer Adresse im Hauptspeicher,
hat hier quasi intern jetzt eben als Informationen gesetzt.
Ich habe jetzt quasi diesen Wert exclusive
In der zweiten CPU wäre jetzt quasi eigentlich dazu dann noch,
dass das also invalled ist, weil da wurde jetzt also nichts drauf zugegriffen.
So, jetzt wird verändert die CPU diesen Wert A. Schreibt da was rein,
macht das aber erstmal nur in ihrem lokalen Cash.
Dann ändert sich jetzt quasi nach diesem Diagramm hier das Ganze dann nämlich in Modified,
das heißt, der weiß jetzt, okay,
ich habe hier was anderes stehen als das,
was irgendwie hier noch im Hauptspeicher steht.
Und jetzt kommen wir eben zu dem Fall,
dass die andere CPU quasi den Wert A lesen möchte
Und diese Leseinformation, also dass jemand anders da so auf diese Adresse drauf zugreift,
die wird eben letztendlich von dieser CPU hier dann eben auch mitprotokolliert,
mitgeschnüffelt, gesnooped. Und das, was da nämlich passiert,
ist, dass diese CPU jetzt also quasi dafür sorgt,
dass also hier dieser Speicher-Transfer gestoppt wird.
Der liefert stattdessen dann quasi aus dem Cash seinen eigenen Wert A,
den er ja quasi modifiziert hat,
an die CPU 2 und würde dann eben Ähm,
beispielsweise dann eben anschließend dann eben auch den Wert A,
in den Hauptspeicher zurückschreiben.
Dadurch hat also quasi diese CPU jetzt hier erstmal vergleichsweise schnell einen gültigen Wert bekommen.
Und hinterher habe ich dann eben auch wieder ein konsistentes System,
mit dem man dann eben entsprechend weiterarbeiten kann.
Also das wäre also die Art und Weise,
wie im Prinzip dann also zum eins von diesen möglichen Protokollen
aussieht und was man also mit diesem Begriff Snooping dann verbindet.
Aber das zeigt dann so ein bisschen diese Problematik,
die dann eben kommt und das wird natürlich quasi umso anspruchsvoller,
je größer meine Systeme sind. Das heißt,
wenn ich jetzt eben so ein hochparalleles Rechnersystem habe,
die jetzt also irgendwie mit 60 CPUs irgendwie auf einen gemeinsamen Speicher draufzugreifen,
dann heißt das eben für die gesamte Speicherverwaltung,
dass also letztendlich jede CPU von allen anderen die Speicherzugriffe mit lesen muss,
um zu gucken, will der jetzt vielleicht irgendwie was lesen,
was ich in meinem lokalen Cash habe.
Und das wird dann eben doch schon
entsprechend aufwendig
Gut, ja, hier nochmal grob skizziert, also wie gesagt, mittleres Speicher Zugriffszeit ergibt sich ja aus diesen Faktoren. Nicht,
wie ich das Ganze jetzt ein bisschen verbessern kann,
entweder eine kleinere Missrate zu machen, das passiert eben bei uns rein praktisch,
dadurch, dass man jetzt mehrere Cash-Ebenen haben,
dann gibt es noch so Mechanismen, sowas,
was sich dann nennt, Critical Word first, heißt,
ich hatte das ja hier in dem Beispiel mit drin,
dass das letztendlich immer, wenn jetzt irgendwie was gelesen wird,
Ich habe zum Beispiel jetzt also hier diese Adresse,
dass also dieser gesamte Bereich irgendwie in den Cash übertragen wird.
Mit Critical World First heißt es dann eben, dass man letztendlich das,
was also genau unter dieser Adresse angefragt wird, eben als erstes überträgt,
damit weitergearbeitet werden kann und im Hintergrund wird dann quasi noch der Rest hier mit aufgefüllt.
Gut, also wie gesagt, da gibt es dann unterschiedliche Mechanismen,
was man dann auch machen kann und ja,
die andere Sache ist dann natürlich die Messrade zu verkleinern,
was dann eben heißt, größere Cashes oder höhere Assoziativität,
was aber dann eben auch wieder heißt,
entweder mehr Hardware oder bei Hörer Assoziativität,
dann werden die Cashes auch wieder ein bisschen langsam machen.
Was man dann häufig noch hat,
sind dann eben noch Optimierungen hinsichtlich der Software,
weil wie gesagt, der Compiler hat natürlich irgendwo den globalen Überblick,
kann dort noch einiges machen und hinsichtlich der Hardware Hatte ich
ja auch schon ein bisschen was gesagt.
Also gerade bei Instruktionen in den Prozessoren werden ja die eigentlichen
Cis-Gefehle jetzt in so eine Folge von Mikrooperationen zerlegt.
Die werden dann eben insgesamt als diese Folge auch abgespeichert.
Und dann eben auch in den Cashes hinterlegt, sodass beim nächsten Mal,
wenn dann eben wieder so ein entsprechender Befehl kommt,
gleich diese fertige Folge abgerufen werden kann.
Also das ist dann also das,
was hier zum Beispiel als Trace Cash bezeichnet ist.
Ja,
ansonsten solche Cash-Effekte
Ganz am Anfang von den Rechnerstrukturen,
Folien ist sowas ja hier mal aufgetaucht,
wo man also quasi diesen klassischen Fall hat,
zwardimensionales Array und ich laufe da entweder zahlenweise oder ich laufe da spaltenweise durch,
wo man dann eben sagen kann, okay,
ich habe zum einen unterschiedliche Werte, was in diesem Fall dadurch kommt,
dass das Fließkummer-Werte sind und jetzt haben wir ja nun auch irgendwie gelernt,
bei Fließkummer-Reth-Metik habe ich eben Rundungseffekte, nicht und abhängig davon,
in welcher Reihenfolge ich das eben mache, wirken die sich unterschiedlich aus,
deswegen gibt es da so quasi unterschiedliche Werte,
so und über diese Arten und Weisen,
wie ich dann eben entweder zahlenweise oder spaltenweise hier durchlaufe,
kann ich eben sagen,
dass es einmal sehr günstig,
weil ich dann eben wenig Cash-Misses habe oder es ist eben je nach Größe der RAs und ungünstige Adressierung habe ich eben maximal viele Cash-Misses,
demzufolge sind dann eben die Laufzeiten der Programme auch unterschiedlich.
Das ist also dann die Erklärung, warum in diesem Fall
jetzt hier bei so einem Programm das Ganze dann also quasi in dieser Zahlenweise Variante eben deutlich schneller wäre, als wenn ich das jetzt also hier andersrum durchlaufen lasse. Gut,
das ist jetzt also auf den folgenden Folien,
also hier nochmal für so ein etwas komplizierteres Beispiel,
wo es dann im Prinzip so ein Markttricks, Multiplikation geht gemacht,
wo man also im Prinzip dann also die unterschiedlichen Zugriffe,
also sprich mit drei Laufvariablen, IJ und K,
entsprechend unterschiedlich schachtelt, werde ich jetzt aber nicht im Einzelnen vorstellen,
wo man dann aber eben sehen kann, also abhängig davon,
wie ich das schachtel, habe ich eben pro Durchlauf,
eben von zwei bis 0,5 Cashmasses, wo man dann eben sagen kann,
es gibt halt bestimmte Varianten, das ist alles der gleiche Algorithmus,
berechnet das gleiche Ergebnis, die laufen halt besonders effizient durch,
es gibt andere, wo ich dann eben wirklich dafür sorge,
dass ich einmal dahin, einmal dahin adressiere und dann entsprechend doch mit einer sehr großen Wahrscheinlichkeit dann eben Cashmisses habe und das Programm dann eben ineffizient wird.
Nicht, wenn man das Ganze jetzt abhängig von der RAI-Größe Dann nämlich durchlaufen lässt,
dann kriegt man nämlich also hier also sehr schöne Kurven,
wo man dann eben sagen kann,
also bei bestimmten Schachtelungen von diesen Laufvariablen verhält sich das so,
wenn ich das, dass ich hier anders schachte,
dann habe ich also hier schon entsprechend höhere Wahrscheinlichkeit für Cashmiths und hier noch höher und dann sieht man nämlich hier also auch schon,
wo sie dann also quasi sprunghaft auf einmal die Zugriffszeiten zunehmen, wo das dann nämlich dazu führt,
dass ich dann also jetzt also auf einmal nicht mehr im First-Level, sondern also auf einmal in Second-Level-Cash lande und dort dann also die gleichen Effekte sich wiederholen.
Also wie gesagt, das kann man dann eben auch jeweils
für die entsprechenden Architekturen untersuchen und dann eben sehen,
okay, also da gibt es Vorteile oder Nachteile.
Ja, ansonsten hier nur noch mal so ein paar von den klassischen Chip-Fotos,
wenn man immer sagt, naja, Cash, was ist das eigentlich?
Also wenn man bei uns also so einen aktuellen oder überhaupt jetzt Prozessoren angucken,
also aktuellen Prozesse darf ich hier jetzt nicht sagen,
aber wenn man sich also die Prozessoren mal ansieht,
das meiste, was wir an Chip-Fläche kaufen,
ist Cash
Also hier arm 7 Prozessor, also gut,
da muss man sagen, das sind irgendwie die 90er Jahre.
Instruktionscash, Daten-Cash, Memory-Management-Unit und hier dieser Bereich und dieser Bereich,
das ist dann eigentlich das, was die CPU ist.
Nicht beim arm 10 Prozessor, auch hier viel Cash,
dass dann die eigentliche Recheneinheit, dann gibt es hier noch,
also im Prinzip nochmal so Sondereinheiten, die dann jetzt,
also für Sektoroperationen im Prinzip genutzt werden konnten, Also auch da muss er.
So bei aktuellen Prozessoren, wobei aktuell stimmt nicht.
Also das, was ich jetzt hier aufliegen habe, ist, stand
2009. Bei den ganz aktuellen ist immer die Sache,
man findet zwar immer irgendwelche Chip-Fotos,
wenn man danach sucht, aber nie jetzt welche,
wo jetzt also wirklich mal so schön aufgezeichnet ist,
wie viel ist Cash, wie viel ist jetzt wirklich eigentliche Recheneinheit.
Nicht deswegen habe ich jetzt also auf den,
die aktuellsten Xeons bzw. i9s verzichtet, sondern also hier noch die etwas älteren Sachen.
Also wie gesagt, hier sind wir im Bereich zu 2009, 2010. Das ist die erste E7-Generation. Nehelem, 4-Kern-Prozessor. Und hier sieht man dann eben auch,
pro CPU habe ich hier First Level Cash für Daten und für Instruktion,
Second Level Cash und dann eben Third Level Cash quasi dann entsprechend diese Teile und okay,
das, was dann hier der Rest ist,
ist dann eben in diesem Fall dann,
also hier die eigentliche Recheneinheit bei der Konkurrenz zu dieser Zeit,
das ist also quasi Optaron, dritte Generation,
bei AMD sieht das also ähnlich aus, nicht,
also auch hier CPU,
First Level Cashes sind dann im Prinzip hier irgendwo noch mit drin Second-Level-Cash und Third-Level-Cash ist dann quasi dieser Teil nicht.
Auch hier sieht man also letztendlich von der gesamten Chip-Fläche ist
deutlich über die Hälfte nur für den Cash benutzt.
Also hier ist es nicht ganz so, aber gut,
nicht kann man sagen, gut,
ihr ist heutzutage bei aktuellen Prozessoren im Prinzip genauso oder ähnlich.
Also da kann man auch sagen entsprechend. Gut,
ja, wie Sie das jetzt eben aus Cash und Programmcode
letztendlich kann man als Programmierer das Ganze bisschen beeinflussen. Nicht weil,
wenn man jetzt weiß, okay, die Datenstrukturen werden quasi fortlaufen,
aduziert, dass man eben sagt,
also ich lege mir meine Datenstrukturen irgendwie passend an,
dass ich da jetzt also quasi in diesen Adressen da jetzt nicht beliebig weit weiter schalte.
Und dass man, wenn man jetzt eben quasi irgendwie so geschachtelt geschleifen hat,
dass man darin dann teilweise solche Techniken anwendet,
wo man dann also irgendwie Blöcke bildet.
Also spreche irgendwie kleinere Teilbereiche hat und dann damit arbeitet
Nicht ansonsten kann man eben sagen, generelle Empfehlung ist also den Working Setline zu halten. Das heißt also,
sprich, wie weit springe ich in meinem Programm,
beziehungsweise wie viel an Daten benutze ich lokal immer,
dass man da eben versucht,
quasi möglichst viel lokal zu halten und jetzt nicht alles zu sagen,
okay, ich habe sämtliche Datenstrukturen, die ich mal brauche,
irgendwie als globale Variablen deklariert und greift dann von jedem Unterprogramm immer auf diese Globalen Variablen drauf zu,
sondern wenn es eben machbar ist und Sinn macht,
irgendwie mit lokalen Variablen, wo man dann eben wirklich sagen kann,
okay, wenn diese Prozedur oder dieser Unterprogrammteil beendet ist,
ist eben auch alles, was dort an Variablen war,
obsolet.
Und ich muss das dann eben nicht mehr zwingend vorhalten.
Damit kann man das machen. Und ja,
bei den Adressen oder Adressfortschaltungen eben die Nutzung dieser räumlichen Lokalität,
also dass man da dann quasi auch lokal letztendlich arbeitet.
Gut, dann werde ich jetzt als letztes Thema auch nur kurz anreichen,
anreisen noch so ein bisschen den Bereich des virtuellen Speichers.
Und zwar ist darunter eben zu verstehen, hat ja schon groß gesagt,
also als Programmierer haben wir eigentlich erstmal vergleichsweise großen Adressraum.
Ich war früher mal 2032 bald. Nicht inzwischen ist man also irgendwie so bei,
ja gut, das ist jetzt ein bisschen von der Architektur abhängig,
also 48 Adressbits bieten die 44-Bit-Maschinen, aber eigentlich alle,
denen ich im Prinzip in meinem Programm adressieren kann.
So, dann kommt natürlich noch hinzu,
dass jetzt also das Betriebssystem ja dann sagt, okay,
ich habe jetzt ja nicht nur ein einziges Programm,
das von dem Mederwasser läuft,
sondern dass es noch irgendwie das und das und das von meinen eigenen Programmen und da laufen also vom Benutzer,
wenn er sich angemeldet hat,
also wegen grafischer Oberfläche noch diverse andere Programme.
Das heißt, dass es dort eben wirklich immer
eine ganze Menge an Tasks gibt,
die da also letztendlich irgendwo arbeiten
Und die Frage ist jetzt eben,
wie kriege ich diese Parallelarbeiten im Prozess jetzt irgendwie voneinander getrennt?
Wie kann ich dann eben sagen, dass Code, Programmcode,
Read only ist und eben nicht im Sinne von irgendwie selbst modifizierendem Programm sich quasi selber umschreiben darf?
Und das eben zu trennen von dem Bereich für Daten,
wo also sowohl gelesen als auch geschrieben wird.
Das führt dann eben dazu, dass man eigentlich zwei mögliche Lösungsansätze hat,
beziehungsweise die werden oder sind eigentlich, jetzt immer gemeinsam eingesetzt,
nämlich einmal etwas, was sich virtueller Speicher nennt und etwas anderes, was Paging ist.
Da kommt jetzt eben letztendlich hinzu, dass man sagt,
insgesamt vom Adressraum kann ich damit eben deutlich mehr adressieren als das,
was eben wirklich in der Hardware verbaut ist.
Demzufolge ist die Festplatte dann eben nichts anderes als eine Erweiterung des Hauptspeichers. Das heißt,
das Betriebssystem kann eben Instruktionen oder Daten von Prozessen,
die jetzt gerade nicht benötigt werden, entsprechend auslagern,
um dann eben zu sagen, ich kann damit dann eben erreichen,
dass also wirklich nur das, was gerade aktiv irgendwo rechnet,
im vielleicht begrenzten Hauptspeicher vorgehalten werden muss.
Was machen wir diesem Mechanismus dann eben auch noch mit umsetzt,
ist dann, dass man dann eben noch unterscheidet zwischen Systemprogramm,
also privilegierten Programm und Benutzerprogramm,
die dann eben unterschiedliche Zugriffsrechte beziehungsweise unterschiedliche Zugriffe haben und dass
man eben bei den Benutzerprogrammen dann eben auch dafür sorgt,
dass sich also jetzt nicht zwei Programme gegenseitig irgendwie mit den Adressräumen entsprechend ins Gehege kommen können.
Gut, also wie gesagt, Grundidee,
Festplatte erweitert letztendlich den Hauptspeicher, weil, wie gesagt,
wenn ich theoretisch sage, ich habe 64 Bit-Adressen,
dann wären das also irgendwie 16 mal 10.16 Uhr byte,
so viel Hauptspeicher Hat man also selten in seinem Rechner,
beziehungsweise der wird rein physikalisch schon gar nicht mehr irgendwo untergebracht werden können. Deswegen,
ja, und das Ganze hat natürlich dann also auch immer noch handfeste wirtschaftliche Voraussetzungen,
weil ich dann eben auch sagen kann, okay,
also wenn ich ein Terabyte speicher, mir also irgendwie kaufen möchte,
dann bin ich also irgendwie so mit 1000 Euro dabei,
wenn ich ein Terabyte-Festplatte kaufe,
dann bin ich schon also mit 30 Euro dabei.
Das heißt, bei entsprechend großen Datenmengen ist es dann also
deutlich günstiger. Gut, also hier nochmal so ein bisschen
die Unterschiede dann aber eben auch hinsichtlich der Geschwindigkeit.
Wie gesagt, CPU mit voller Prozessorgeschwindigkeit. Das heißt also
irgendwie so ein Gigahertz-Bereich. Nicht dann beim Cash-Speicher,
je nachdem, Opkurs and First Sanctuar, Third-Level-Cash ist,
dann eben von einem bis irgendwie so einige wenige Takte,
Geschwindigkeit, Hauptspeichern eben schon deutlich langsamer und fest Plattenspeicher dann wirklich um Größenordnung langsamer.
Auf der anderen Seite muss man dann eben sagen,
Reise sind natürlich dann eben auch entsprechend verteilt. Gut,
hier sieht man dann eben auch nochmal,
wie also im Prinzip First-Level-Cash ist, nicht bei direkten Zugriffen,
dauert das eben ein bis zwei Prozesse, Zyklen,
Misspenalty wären dann also im Prinzip so bis zu 100 Zyklen,
die ich dann letztendlich brauche.
Wenn das jetzt alles geschickt aufgebaut,
das liegt also die Missrate dann eben typischerweise deutlich unter zehn
Prozent und man kann mit dem Cash,
also sagen wir jetzt in Bezug auf das Programm
dann so einen Adressraum so von 14 bis 20 Bit abdecken
So, wenn ich jetzt in meinem Programm noch mehr Daten habe,
dann kann ich schon mal davon ausgehen, die werden nicht im Cash liegen können,
weil dann fürs The Cash dann einfach nicht groß genug. Gut,
die liegen dann also irgendwo im Hauptspeicher und wenn ich jetzt also noch mehr brauche und die dann also quasi auf die Festplatte als Virtual Memory ausgelagert sind,
nicht dann hat man dann eben entsprechend große Zeiten für die Zugriffszeit,
kann dann aber eben auch entsprechend große Adressbereiche mit ansprechen und Was man dann eben typischerweise haben sollte,
ist dann eben, oder auch haben wird,
ist dann eben entsprechend sehr, sehr kleine Missrate,
dass also wirklich dieser Fall,
dass irgendwelche Daten auf die Platte ausgelagert werden,
dann eben entsprechend selten vorkommt.
Gut, hier nochmal die unterschiedlichen Zugriffszeiten,
also die ARAM ist ungefähr Faktor 10 langsamer als SS-Rahmen.
SRAM ist eben so knapp ein bis zwei Zyklen über Prozessorgeschwindigkeit,
nicht in beider Festplatte ist man dann im Bereich von
einigen hunderttausend bis
zu Millionen langsamer
Gut, was dabei dann ja passiert,
hatte ich ja eben schon kurz angedeutet,
ist dann also quasi diese Adressumrechnung durch diese Memory Management Unit.
Das heißt, jeder Prozess hat seinen eigenen Adressraum und
die virtuellen Adressen werden dann eben in physikalische Adressen umgerechnet.
Und typischerweise hat man dann eben letztendlich hier davor den First
Level Cash hier hinter dann eben Second Level und Third
Level Cash
So, und was dann eben hier auch noch passiert ist,
dann eben letztendlich die Überprüfung dieser gesamten Zugriffsrechte bei der Berechnung der Adressen,
das dann eben geguckt wird, wenn ich jetzt eben auf physikalische Adressen gehe,
darf dieser Prozess überhaupt darauf zugreifen.
So, was passiert dort in der Regel? Letztendlich wird in dieser Memory Management Unit eigentlich nur eine Tabelle geführt,
wo ich dann eben sage,
für jeden Prozess habe ich sowas wie eine Art Anfangsadresse und irgendwelche Offsets und das,
was ich dann immer mache, ist ja,
dass ich eben einfach jetzt diese Anfangsadresse mit den Adressen,
die jetzt also quasi im Programm stehen, addiere,
um dann quasi jetzt zu irgendwelchen physikalischen Adressen zu kommen.
Was dann dabei dann auch immer noch passieren kann, ist,
dass dieser gesamte Adressbereich dann eben noch in bestimmte Bereiche unterteilt wird, in sogenannte Blöcke,
die dann entweder im Hauptspeicher oder ausgelagert auf der Festplatte rumliegen.
Das ist also das,
was man dann also quasi bei Windows als Auslagerungsdatei hat,
beziehungsweise bei Unix-Systemen entweder in Form einer Swap-Partition oder in Form von Swap-Dateien oder beides.
Ja. Gut, ohne diesen virtuellen Speicher umzusetzen. Wie gesagt,
zwei Konzepte, Segmentierung und Paging, die inzwischen eigentlich auch seit 15 Jahren gemeinsam angewandt werden.
Was muss man sich darunter vorstellen? Also mit Segmentierung ist einfach grob die Idee,
dass ich sage, Ich habe hier zwei Prozesse,
Prozess 1 und Prozess 2. Jeder von denen geht einfach erst mal,
weil das ist ja das,
was der Compiler daraus macht oder Assembler, geht ja davon aus,
also ich starte hier bei Adresse 0,
dann habe ich hier mein Textsegment, Text ist quasi der Instruktionscode,
also sprich der Programmcode,
also der Samlercode und dann kommen die Daten,
also sprich das,
was ich jetzt hier in irgendwelchen Variablen
mir deklariert habe und das geht dann eben bis zu irgendeiner
bestimmten Adresse
So ein zweiter Prozess, genau das Gleiche, auch da hat der Compiler gesagt,
okay, von null bis irgendwas geht also dein Textsegment und von hier bis irgendeinem Offset geht dann also dein Datensegment.
So und das, was eigentlich passiert, ist,
wenn ich jetzt also diese virtuellen Adressen in physikalische Adressen umrechne,
ist, dass ich sage, okay,
die werden jetzt irgendwie in freie Teile des Hauptspeichers abgebildet,
wo man einfach sagt, für jeden Prozess gibt es letztendlich eine Tabelle,
wo ich sage, ich nehme die Adresse addiere jetzt also quasi die Basisadresse,
entweder für Text oder für Data hinzu.
Das heißt also aus dieser Null wird dann eben eine 10.000,
du warst dann also in diesem Fall dann also quasi jetzt hier heißen würde,
ich muss dann also plus 10.000 rechnen, dann habe ich die physikalische Adresse.
Beim Datenbereich müsste ich dann also quasi jetzt hier die Adresse plus dieser Startadresse dann eben entsprechend rechnen,
beziehungsweise dann nochmal minus 1.000, ich habe jedenfalls mit einem bestimmten festen Offset,
kann ich dann eben letztendlich, also aus diesen Adressen,
diese Abbildung entsprechend machen.
Gut, für den zweiten Prozess eben genau das Gleiche.
Und dadurch können dann im Prinzip von mehreren Prozessen,
Daten und Instruktionen auf den Hauptspeicher verteilt werden.
Und was dabei dann eben noch passiert, gleichzeitig ist,
dass ich jetzt für jeden Prozess quasi diese Basisadresse
irgendwo mit abspeichern muss
Und was man da dann eben machen kann, ist,
dass man sagen oder machen muss, ist,
dass man sagt, es gibt eben eine Obergrenze.
Das heißt, wenn ich jetzt also quasi hier eine Textadresse von Prozess 1 habe,
wird dann eben hier irgendwo stehen als Obergrenze tausend und Adressen größer tausend,
sind dann eben auch keine gültigen Adressen mehr.
Und es sollte dann eben irgendeine Ausnahmebehandlung angestoßen werden, die sagt,
ja, du greifst hier aber auf irgendwelche Adressen darauf zu,
die dir gar nicht gehören.
Und gleichzeitig kann man dann eben auch noch irgendwie Zugriffsrechte damit verbinden,
nämlich dass man sagt, Dieses hier,
also sprich die Adressen, von hier bis hier,
also dieses Text-Segment ist eben read only und er darf jetzt nicht reingeschrieben werden,
zum Beispiel.
Also all das lässt sich dann letztendlich mit diesem Mechanismus so abbilden.
Nach Taba dem Ganzen ist, Prozess wird beendet,
diese Speicherbereiche werden hier irgendwie dann wieder freigegeben.
Andere laufen noch und dann passiert eben etwas,
was man hier so ein bisschen sieht,
dass es eben zwischen den Speicherbereichen hier eben irgendwie willkürlich
große oder kleine
Lücken gibt
Und irgendwann kann es eben sein,
dass ich einen Prozess habe, der theoretisch laufen könnte,
weil eigentlich hier noch genügend Speicher dabei ist.
Der ist aber eben so fragmentiert,
dass wenn der eben beispielsweise jetzt sehr viele Daten beinhaltet,
dass der also da nicht mehr abgebildet werden kann.
Aber das ist eben die Art und Weise,
wie jetzt also im Prinzip die Segmentierung arbeitet,
nämlich also die Trennung von Instruktionen und Daten und im Prinzip von Stack,
der ist jetzt hier noch gar nicht weiter aufgeteilt,
würde aber im Prinzip
genauso dort dann also auch mit berücksichtigt werden
Ja, Vorteil, wie gesagt,
man hat in den Segmenten immer die logisch Zusammengehörigen da,
kann dort sehr gut eigentlich diese Zugriffsrechte trennen,
Speicherschutz realisieren und kann eben diese Segmentgrenzen dann eben auch durch die Obergrenzen,
die ich ja quasi hier raus lesen kann,
die dann also hier Daniel Firt Text und Data beziehungsweise dann
eben auch noch für Stack dann irgendwie mit abgelegt werden,
sehr gut prüfen.
Nachteil, die Segmente können,
da fehlen Nennen,
sehr groß werden
Demzufolge dann eben, wenn ein Prozess zwischenzeitlich rausgeworfen wird,
neu ausgesetzt wird, kann das also dann dies ein- und auslagern von diesen Segmenten
sehr lange dauern und was eigentlich der gravierende Nachteil ist.
Man kann sich da dann eben natürlich dann immer irgendwelche Beispiele konstruieren,
wo man also einen Verschnitt hat,
wo ich dann also mehrere kleinen Adressbereiche noch überhabe,
die dann aber immer zu klein sind, um irgendwelche Programme laden zu können.
So, das führt dann nämlich zu der Idee des Paging,
was dann nämlich also auch gleich einhergeht mit dem Auslagerung auf die Festplatte,
dass man sagt, es gibt Im Adressraum, Blöcke einer festen Größe,
die nennen wir typischerweise an Seiten oder eben Pages genannt.
Daher kommt also der Begriff. Und es gibt im Hauptspeicher
dann eben entsprechend genauso große Blöcke, die nennt man an Kacheln.
So, und das, was passiert, ich habe in der CPU
die virtuellen Adressen, die greifen zu auf eine Page-Table.
Dort steht dann eben entsprechend der Verweis für diese Adresse,
wo die im Hauptspeicher zu finden ist und unter welcher Startadresse.
Und Es ist ja dann eben ein fester,
zugeordneter Bereich, wo man dann eben wirklich weiß,
okay, von da bis da,
das heißt also diese Größe oder die hier wieder ein bisschen unmotiviert auf das Touchpad gekommen,
die Sache mit den Obergrenzen hat man dadurch dann eben entsprechend auch mit erschlagen.
So, was man dort dann eben auch mitsieht,
mitmachen kann, dass wenn jetzt von irgendwelchen Prozessen,
die gerade nicht laufen, die Sachen auf die Festplatte ausgelagert werden,
dass es dann eben hier entsprechende Einträge gibt,
die dann eben sagen,
okay,
das ist jetzt irgendwo der und der Block auf der Festplatte
und gerade nicht im Hauptspeicher abgebildet ist
Sowas hierbei dann nämlich auch vergleichsweise gut möglich ist, ist etwas,
was, ich muss ja mal kurz die Grafik dazu finden.
Was man hier dann nämlich also auch noch sieht,
ist, dass also im Prinzip Prozesse Daten gemeinsam nutzen können.
Das ist also zum Beispiel sinnvoll für Bibliotheksfunktionen, die quasi vom Betriebssystem,
also beispielsweise jetzt hier unter Linux irgendwie sowas wie eine LipC oder mathematische Bibliothek,
die vom Betriebssystem bereitgestellt werden,
die dann quasi als Shared Libraries zur Laufzeit geladen werden,
nicht wo man dann eben sagt, okay,
die entsprechenden Adressen kann ich dann eben wirklich
Hier liegt dann eben Talbibliotheksfunktion, der sowohl von dem einen Prozess
als auch von dem anderen Prozess letztendlich angesprochen werden kann.
Nicht darüber ist das Ganze dann eben auch entsprechend gut möglich.
Gut, also wie gesagt, Paging ist eben dann letztendlich,
also die Abbildung von Adressen auf Hautspeicher und Festplatte.
Demzufolge kann ich damit eben Programme implementieren, die deutlich größer sind als der Hauptspeicher.
Die können dann eben entsprechend dieser Tabellenzuordnung beliebig irgendwie in den Speicher abgebildet werden. Dadurch,
dass sie eine feste Seitengröße habe,
ist das dann also vergleichsweise einfach zu handhaben,
weil man dann eben einfach sagt,
also es werden einfach nur so und so viele Bitteradresse genommen und damit wird das Ganze dann nämlich passend adressiert.
Und ich kann dann eben auch für jede Seite einzelnen,
also Zugriffsrechte haben, also soll jetzt nur gelesen werden oder darauf,
worauf geschrieben werden, ist das ein Benutzerprozess oder gehört da quasi privilegiert dem Betriebssystem.
Solche Sachen, wie gesagt, das,
was ich gerade eben schon skizziert hatte,
solche gemeinsam genutzten Programmteile,
können dadurch eben
entsprechend einfach integriert werden
So rein praktisch sieht das typischerweise so aus,
dass das nicht nur ein einstufiges, sondern mehrstufiges Verfahren ist.
Dass es also hier mal so grob angedeutet,
ist also schon auch seit 486er CPUs,
also sprich seit den 90er Jahren letztendlich so der Fall,
wo man gesagt hat, hier in 32 Bit-Adressraum.
Es gibt jetzt also im Prinzip so eine Art Basisadresse,
wo jetzt also quasi hier erst mal zehn Bit genommen werden,
die zeigen dann also auf einen Page da Ractory,
was dann also quasi relativ zu dieser Basisadresse hier irgendwo einen Eintrag hat.
Da steht jetzt also die Basis für die Page Table und eine 20-Bit-Adresse,
die dann quasi um zehn Bit in der Table nochmal,
also in diesem Fall mit 1024 Einträgen dann also quasi ergänzt wird,
dann habe ich hier quasi jetzt die eigentliche Basisadresse von der Page und dann eben eine 20-Bit-Adresse,
die dann also quasi mit diesem 12-Bit-Off-Set dann zu einer 32-Bit-Adresse dann wieder jeder auf abgebildet wird.
So, damit kann man dann eben entsprechend große Adressräume bearbeiten,
kann das dann eben auch jetzt für mehrere Prozesse untereinander teilen,
hat letztendlich hiermit auch noch mit drin,
also das fehlt jetzt hier als einen Tag,
also eben diese Informationen,
welcher Prozess was darf
Nachteil bei dem Ganzen ist, was man hier auch schon sieht,
das ist also ein vergleichsweise umständliches Verfahren. Das heißt,
also es ist jetzt nicht mehr so,
dass ich sage, wunderbar,
in meinem Programmcode steht also irgendwie so hexadezimal so eine Adresse und damit adressiere ich jetzt,
zack, einmal in den Hauptspeicher rein,
sondern das wird irgendwie umgerechnet in einer Tabelle erstmal nachgesehen,
daraus die nächste Adresse, dann auch in der nächsten Tabelle wieder nachgesehen,
bis ich dann also wirklich mal bei einer physikalischen Adresse lande.
Das ist also dann in Bezug auf den Zugriff natürlich,
also vergleichsweise ineffizient und funktioniert eben auch nur dadurch,
dass im Prinzip diese Umrechnung dann eben auch nochmal in speziellen Cashes quasi zwischengespeichert werden,
sodass man solche Umrechnungen dann nicht jedes Mal für jeden Adresszugriff einzeln neu machen muss.
Aber das ist also die Art und Weise,
wie das inzwischen eigentlich bei den Prozessoren
typischerweise stattfindet
In dieses Konzept kann man dann eben letztendlich,
also die Segmentierung auch noch mit reinnehmen, dass man eben sagt,
okay, Segmentierung war, dass sich das jetzt also quasi nach Prozessen aufteile.
Jetzt habe ich aber hier im Prinzip dieses Paging mit den Seiten,
dass man dann eben sagt,
ich habe eigentlich eine Zuordnung von für jeden Prozess,
wo ich dann eben sagen kann,
dessen Speicherbereich wird dann eben auf diese Seite und diese Seite aufgeteilt,
von dem zweiten Prozess habe ich dann eben hier und hier irgendwie meinen Bereich.
Das heißt, man kann eben sowohl dieses Segmentierungsprozess Konzept als auch eben dieses Paging,
das heißt, was liegt auf der Festplatte und eben eine
freie Zuordnung über so einen Tabellenmechanismus in physikalische Adressen im Hauptspeicher,
das dann eben noch beliebig miteinander mischen.
Gut, ja, was dabei dann eben auch,
wie gesagt, hier sieht man jetzt,
wie das dann aussieht,
also wenn es Seiten Fehler sind,
das von der PES-Festplatte geladen wird,
nicht was da dann eben letztendlich dann eben auch noch
mit reinkommt in diese Tabellen,
sind dann eben
diese Sicherheitsabfragen und Sicherheitsmechanismen
Gut, auf den folgenden Seiten ist im Prinzip dieses Ganze,
also sprich letztendlich,
dieses hier nur noch mal ein bisschen ausführlicher für X86 Architektur,
also letztendlich sowas hier, beschrieben nach dem Buch von Brian und Heileran,
liefert in dem Sinne jetzt aber keine neue Information,
deswegen werde ich das, mir das jetzt also eigentlich auch sparen,
das was eigentlich von dem, womit ich jetzt hier abschließe,
also noch irgendwo hängen bleiben sollte, ist eher die Sache,
dass man sagt, okay, was ist Cash-Speicher,
wie wird er eingesetzt,
welche Arten von Cashes gibt es und eben in Bezug auf virtuellen Speicher,
dass man damit eben im Prinzip mehr Speicher erstmal generieren und unterteilen kann als das,
was letztendlich das Betriebssystem hat und dass man damit dann eben
diese Schutz- und Zugriffsmechanismen mit umsetzen kann und das eben über diese Tabellenartige Abbildung Ebene Trennung in Speicherbereiche aus unterschiedlichen Prozessen passiert und eben auch eine beliebige Verteilung von diesen Daten dann eben wirklich auf physikalischen Speicher,
der entweder im Hauptspeicher irgendwo liegt oder auf die Festplatte ausgelagert ist.
Gut, damit kann man sagen, Zusammenfassung zu Ende,
Literaturliste und ist jetzt in dem Fall also auch wirklich Ende
der Folien
Von daher bin ich jetzt mit rechner Struktur und so weiter am Ende.
Zeitlich passt das auch ganz gut. Ich weiß,
es war also gerade jetzt, dass ich hier in diesem letzten Teil,
in diesem Bezug auf die Rechnerarchitektur doch relativ viel an Informationen in relativ kurzer Zeit.
Da kann ich eben immer nur sagen,
sorge so ein bisschen als Motivation sein,
weil das uns natürlich jetzt bei der Programmierung bzw. im Umgang
mit Rechnern rein praktisch dann also auch noch entsprechend betrifft.
Ansonsten in Bezug auf die, Klausur hatte ich ja auch schon gesagt,
kann ich also zumindest bei sowas dann hier Entwarnung geben.
Also da wird jetzt also nicht irgendein Beispiel mit irgendwie einfach assoziativen Cashes durchgerechnet in der Klausur,
sondern da bleiben wir dann also eigentlich bei dem,
was dann auch in der Übungsaufgaben entsprechend mit dran gekommen ist.
Trotzdem hoffe ich, dass das Ganze zumindest jetzt,
also diese letzten Kapitel Pipelining-Parallel-Architekturen, Speicher, Hierarchie,
sowas, dass das nochmal so ein bisschen motiviert hat,
also auch nochmal so ein bisschen über die Hardware,
mit der wir also tagtäglich arbeiten, nachzudenken,
beziehungsweise Grundidee erstmal dafür gegeben hat,
was da also eigentlich rein praktisch dann noch an zusätzlichen Schwierigkeiten mit hinzukommt.
Die wird glücklicherweise in der Regel nicht irgendwie aktiv sehen,
beziehungsweise mit denen wir uns dann nicht meistens nicht direkt rumschlagen müssen.
Ja, das von mir jetzt alles. Von daher,
dann Semester ist zu Ende. Vorlesen ist auch zu Ende
. Ich denke mal, die meisten bin ich dann irgendwie
bei der einen oder anderen Klausur wiedersehen,
kann da dann eigentlich nur allen Gutes gelingen wünschen
Wie gesagt, es wird dann nächste Woche noch eine Probeklausur geben mit Besprechung,
Anfang über nächste Woche dann ja nochmal dieses Tutorium,
wo also im Prinzip nochmal allgemeine Fragen gestellt werden können.
Also beliebig über den Stoff verteilt und ich hoffe,
dass dann zumindest alle,
die sich jetzt also in der ersten Anmeldephase dann also angemeldet haben,
auch so weit fit sind,
dass sie dann in der übernächsten Woche dann die Klausur mitschreiben können.
Ja,
danke und tschüss