Hallo zusammen, in diversen Büchern habe ich gelesen, dass RISC-Prozessoren eine LOAD-STORE-Architektur zugrunde liegt. Das bedeutet nach meinem Verständnis, dass - wenn im Assemblerprogramm in einer arithmetischen Operation eine Variable verwendet wird, die sich noch im Hauptspeicher befindet - auf diese nicht direkt zugegriffen werden kann, sondern, dass sie erst mit dem LOAD-Befehl in eines der internen Register geschoben werden muss, wo sie dann mit schnellen arithmetischen Register-Register-Operationen weiterverarbeitet werden kann. Ich frage mich, ob ich das richtig verstanden habe und ob daraus ein Geschwindigkeitsvorteil resultiert. Über Antworten würde ich mich freuen. Vielen Dank.
Torben schrieb: > Ich frage mich, ob ich das richtig verstanden habe und ob daraus ein > Geschwindigkeitsvorteil resultiert. Ein Geschwindigkeitsvorteil gegenüber was?
Gegenüber des direkten Speicherzugriffes.
CISC/RISC ist heute längst obsolet, weil auch CPUs mit CISC-Befehlssatz wie x86 intern RISC-CPUs sind, die die externen Befehle in Mikro-Ops aufspalten und diese dann ausführen. Umgedreht hat ARM bei Cortex-M auch mit Thumb-2 einen Mischbefehlssatz mit variabler Länge, was klassischem RISC widerspräche. Aber bei rein 32-bit ist die Codedichte mau (zuviel Bloat) und bei rein 16 bit die Performance (oftmals zwei Befehle statt einem). Was aber für einen RISC-Befehlssatz spricht, ist die Tatsache, daß es weitaus leichter ist, ein optimierendes Compiler-Backend dafür zu schreiben. Das ist insbesondere für einen neuen Befehlssatz wie RISC-V wichtig, denn ohne Toolchain ist eine CPU wertlos. Zwar gibt es für x86 mt komplexem Befehlssatz auch gute Compiler-Backends, aber die sind historisch gewachsen. Für eine neue CPU hätte man jetzt nicht die Zeit, da noch Mann-Jahrhunderte reinzustecken.
Üblicherweise sind tatsächlich bei RISC, durch die einfacheren Instruktionen, höhere Taktraten erreichbar. Auch sind diese Architekturen als Zielarchitekturen für Compiler einfacher zu handhaben¹. Andererseits braucht man viele Register, um zu häufigen Speicherzugriff zu vermeiden. Das führt dazu, dass RISC üblicherweise schnell ist, aber Nachteile bei der Codegröße hat. Allerdings ist der Übergang fließend, und auch Prozessoren, die auf Ebene der ISA CISC sind, sind oft auf Mikroarchitekturebene RISC. Bei einer RISC-ISA wird die Übersetzung zwischen ISA und Mikroarchitektur allerdings viel einfacher (und schneller), da direkt in Hardware umgesetzt, nicht durch ein Mikroprogramm. Philipp ¹ Ein Beispiel für einen typischen CISC-Befehl wäre cpir beim Z80. Diesen vom Compiler verwenden zu lassen, würde eine sehr aufwendige Analyse des Programms erfordern. So wird dieser Befehl z.B. von SDCC bisher nicht verwendet (außer für strlen aus der Standardbibliothek).
Philipp Klaus K. schrieb: > Andererseits braucht man viele Register, um zu häufigen > Speicherzugriff zu vermeiden. Hier wird es für mich interessant. Warum vermeidet man häufige Speicherzugriffe? Nach der RISC-Philosophie muss man doch die Variablen vorher aus dem Speicher holen, um sie in interne Register abzulegen. Dabei sind doch genauso viele Speicherzugriffe notwendig, wie bei einer Register-Speicher-Architektur.
Torben schrieb: > Philipp Klaus K. schrieb: >> Andererseits braucht man viele Register, um zu häufigen >> Speicherzugriff zu vermeiden. > > Hier wird es für mich interessant. Warum vermeidet man häufige > Speicherzugriffe? Nach der RISC-Philosophie muss man doch die Variablen > vorher aus dem Speicher holen, um sie in interne Register abzulegen. > Dabei sind doch genauso viele Speicherzugriffe notwendig, wie bei einer > Register-Speicher-Architektur. Betrachten wir nur lokale Variablen, da diese eh am häufigsten vorkommen und am häufigsten verwendet werden (neben den explizit vom Programmierer deklarierten gibt es noch weitere, temporäre für Zwischenergebnisse). Diese müssen ja irgendwo gespeichert werden. Üblicherweise auf dem Stack (erfordert Speicherzugriff) oder in Registern. Je mehr Variablen in Register passen, desto weniger Speicherzugriffe sind also nötig. Bei RISC hat man einen orthogonalen Befehlssatz, typischerweise so, dass die meisten Befehle drei Operanden (ein Ziel, zwei Quellen) haben. Alle davon sind Register, und der Befehl ist gleichschnell, egal auf welche Register er zugreift. Damit lassen sich alle Register flexibel verwenden (auf einfache Art für den Compiler, z.B. durch einen graphfärbenden Registerallokator nach Chaitin - es kommt ja nur darauf an, ob eine Variable in einem Register liegt, nicht darauf, in welchem).
Torben schrieb: > Ich frage mich, ob ich das richtig verstanden habe und ob daraus ein > Geschwindigkeitsvorteil resultiert. Also diese Frage noch frisch war, d.h. in der 90ern, ergab sich das aus der Möglichkeit eines Compilers, die Wartezeit von Start der Ladeoperation bis Verwendung des Registers mit anderen Befehlen zu füllen. Mittlerweile machen das die Highend-Prozessoren durch interne Befehlsumordnung selbst. Prozessoren wie Cortex A55 sind davon noch betroffen.
Torben schrieb: > Hier wird es für mich interessant. Warum vermeidet man häufige > Speicherzugriffe? Aufgrund der Zugriffszeit: Register: 1 Takt, L1-Cache: 4-5 Takte, DRAM: Grössenordnung 200 Takte. > Nach der RISC-Philosophie muss man doch die Variablen > vorher aus dem Speicher holen, um sie in interne Register abzulegen. Vorausgesetzt sie sind nicht schon im Register. im Idealfall bleiben lokale Variablen in Registern, mindestens zeitweilig, sehen überhaupt keinen Speicher. Weshalb RISCs meist eine deutlich grössere Anzahl Register haben.
:
Bearbeitet durch User
Philipp Klaus K. schrieb: > Betrachten wir nur lokale Variablen, da diese eh am häufigsten vorkommen > und am häufigsten verwendet werden (neben den explizit vom Programmierer > deklarierten gibt es noch weitere, temporäre für Zwischenergebnisse). > Diese müssen ja irgendwo gespeichert werden. Üblicherweise auf dem Stack > (erfordert Speicherzugriff) oder in Registern. > Je mehr Variablen in Register passen, desto weniger Speicherzugriffe > sind also nötig. Danke für die gute Erklärung. Das kann ich nachvollziehen und habe es jetzt verstanden. Dürfte ich noch eine weitere Frage dazu stellen? Müssen die vom Compiler erzeugten Maschinenbefehle, die ja nach meiner bescheidenen Vorstellung irgendwo im Hauptspeicher liegen müssten, nacheinander vom Hauptspeicher in das Befehlsregister des Prozessors übertragen werden und und sind dafür nicht auch Speicherzugriffe notwendig?
Torben schrieb: > Müssen die vom Compiler erzeugten Maschinenbefehle, die ja nach meiner > bescheidenen Vorstellung irgendwo im Hauptspeicher liegen müssten, > nacheinander vom Hauptspeicher in das Befehlsregister des Prozessors > übertragen werden Ja, aber nicht nacheinander. > und und sind dafür nicht auch Speicherzugriffe notwendig? Der Trick ist, daß die mit einer Art Vorauslesen übertragen werden, dafür ist die Cache-Pipeline da. Bei sequentiellen Befehlen ist das sehr leicht möglich, während bei bedingten Sprüngen eine Sprungvorhersage zum Einsatz kommt. Das Vorauslesen ist deswegen schneller, weil man dabei nicht bloß einen Befehl überträgt, sondern gleich einen ganzen Batzen. Die Latenz ist dabei ja dieselbe, verteilt sich dadurch aber auf mehr Befehle. Man kann bei einem bedingten Sprung sogar beide Fälle der Verzweigung spekulativ laden und ausführen, und dann einen der beiden wieder verwerfen. Allerdings kann das ungewollte Seiteneffekte haben, die uns die ganzen Sicherheitslücken bei Intels x86ern beschert haben, in minderem Grad aber auch bei AMD und ARM.
A. K. schrieb: > Aufgrund der Zugriffszeit: > Register: 1 Takt, > L1-Cache: 4-5 Takte, > DRAM: Grössenordnung 200 Takte. Was ist mit Takt gemeint? Etwa die Schrittgeschwindigkeit, also der minimale zeitliche Abstand zwischen zwei Spannungspegeln?
Torben schrieb: > in diversen Büchern habe ich gelesen, dass RISC-Prozessoren eine > LOAD-STORE-Architektur zugrunde liegt. Das mag zwar sein, ist aber nicht bezeichnend für RISC. RISC heißt im Klartext lediglich, daß die Anzahl der veschiedenen Maschinenbefehle, die die CPU kennt, eben _R_eduziert ist. Also weniger verschiedene Befehle als bei CISC. Damit so eine CPU dann nicht doof dasteht im Vergleich zu CISC, müssen die verbleibenden Befehle eben anders organisiert und vielseitiger sein als bei CISC. Und das erfordert dann einen darauf abgestimmten Registersatz in der CPU. Aber Load-Store ist eine ganz andere Sache. Das bedeutet lediglich, daß das Verändern von Variablen im Daten-Speicher etwa so geht: gewünschte Operation: ottokar = ottokar + 1; 1. Lade ottokar aus Speicher in CPU-Register R 2. addiere 1 zu R 3. Speichere R zurück nach ottokar Das hat nix mit RISC zu tun. Schau dir mal als Gegenbeispiel die Architektur der kleinen PIC's an. Die sind in ihrer Art auch RISC, haben aber keine Load-Store Architektur, sondern erledigen das o.g. Verändern von Variablen direkt im Datenspeicher. Bei denen geht obiges Beispiel dann so: 1. INCF ottokar,F und fertig. So etwas setzt eine Harvard-Architektur voraus, wenn es effektiv stattfinden soll. Denn damit können das Befehls-Holen und Ergebnis-Speichern parallel zur gleichen Zeit stattfinden. Und nochwas zum Thema Register in der CPU: Es ist klar, daß man damit umso mehr Variablen in der CPU halten kann, je mehr Register man dafür hat. Aber die allerwenigsten Funktionen kommen ohne Aufruf von weiteren Funktionen aus, weswegen bei Funktionsaufrufen auch mehr Register auf dem Stack gerettet werden müssen. Sowas ergibt dann einen doppelten Zeitbedarf (rauf auf den Stack und hinterher wieder runter). Viele Register in der CPU sind also nicht per se auch besser als wenige Register, sondern es ist immer eine Art Grauzone. Deshalb ist der folgende Satz nicht wirklich richtig: Philipp Klaus K. schrieb: > Je mehr Variablen in Register passen, desto weniger Speicherzugriffe > sind also nötig. Siehe oben. Es kommt also auf die konkrete Situation und die Konstruktion der CPU drauf an. Torben schrieb: > Müssen die vom Compiler erzeugten Maschinenbefehle, die ja nach meiner > bescheidenen Vorstellung irgendwo im Hauptspeicher liegen müssten, > nacheinander vom Hauptspeicher in das Befehlsregister des Prozessors > übertragen werden und und sind dafür nicht auch Speicherzugriffe > notwendig? Ob Compiler oder Assembler, in jedem Fall müssen die abzuarbeitenden Befehle aus dem Befehlsspeicher geholt werden (oder aus dem Cache, wenn dort vorhanden). Bei v.Neumann ist das zugleich auch der Datenspeicher. Siehe Rechner-Architekturen. Und ja, auch dafür braucht es Speicherzugriffe. Und je schmaler ein Befehlssatz ist, desteo weniger Bits braucht man dafür. Wenn ein Speicher 32 Bit breit ist, dann würde da bei einem Zugriff 1 Befehl á 32 Bit oder eben 2 Befehle á 16 Bit mit einem Rutsch zu holen sein. W.S.
Torben schrieb: > Ich frage mich, ob ich das richtig verstanden habe und ob daraus ein > Geschwindigkeitsvorteil resultiert. Richtig verstanden, wuerde sagen, ja. Voellig unabhaengig davon, ob jetzt RISC oder nicht: Aufeinanderfolgende LOAD/STORE sind nicht effektiv und sind natuerlich, atomar gesehen (als einzelner Befehl) langsamer als eine Register-Operation. Der Geschwindigkeitsvorteil einer CPU kommt daher alleine durch's Pipelining und natuerlich durch die maximale Taktfrequenz zustande. Gibt hier als Bsp. etwas ASCII-Art zu einem LOAD vom internen memory: https://section5.ch/index.php/2019/09/27/risc-v-experiments/ Im Fall, wo das RAM einen Wartezyklus braucht (READ_WAIT), bis die Daten nach Anlegen der Adresse valide sind, ist es demnach unguenstig, sie gleich weiterzuverarbeiten, besser ist es, wenn der Compiler die Befehle so anordnet, dass ein LOAD frueh stattfindet und waehrend des Totzyklus des Memory eine andere Operation ausgefuehrt werden kann. Das Optimum - fuer eine einfache Pipeline - ist, ohne Instruction-Reordering (zur Laufzeit) einen Taktzyklus pro Operation zu brauchen. Kann man natuerlich mit superskalaren Designs noch weiter auf die Spitze treiben. Nop schrieb: > Zwar gibt es für x86 mt komplexem Befehlssatz auch gute > Compiler-Backends, aber die sind historisch gewachsen. Für eine neue CPU > hätte man jetzt nicht die Zeit, da noch Mann-Jahrhunderte reinzustecken. Liegt auch bei GCC nicht gerade bei Mann-Jahrhunderten, mein letztes Projekt hat ca. ein Mannjahr (plus ca. 3 sinnlos outgesourcte Fernost-Mannjahre, d.h. fuer die Tonne) gekostet. Mit LLVM kocht sich's noch weiter runter.
Irgendwie meine ich mich zu erinnern, dass es für einige C-Compiler eine "fast" Definition für Variablen gibt: https://stackoverflow.com/questions/46959685/why-would-uint32-t-be-preferred-rather-than-uint-fast32-t/46960470 Gab's nicht auch eine, mit der der Compiler aufgefordert wird, die Variable im Register zu halten?
W.S. schrieb: > Das mag zwar sein, ist aber nicht bezeichnend für RISC. Martin S. schrieb: > Der Geschwindigkeitsvorteil einer CPU kommt daher alleine durch's > Pipelining und natuerlich durch die maximale Taktfrequenz zustande. Kann ich dann folgenden Schluss ziehen?: Prozessoren, die eher an der RISC-Philosophie angelegt sind, verfügen über kurze elementare Befehle. Infolgedessen ist das Maschinenprogramm länger, das z. B. der Compiler generieren würde. Daraus ergeben sich dann auch viele Speicherzugriffe, weil jeder einzelne Assemblerbefehl aus dem Hauptspeicher geholt werden muss. Diese Speicherzugriffe wirken sich aber schlecht auf die Performance aus. Geheilt wird dieses Problem durch Pipelining.
W.S. schrieb: > gewünschte Operation: ottokar = ottokar + 1; > 1. Lade ottokar aus Speicher in CPU-Register R > 2. addiere 1 zu R > 3. Speichere R zurück nach ottokar W.S. schrieb: > Architektur der kleinen PIC's an. ... > 1. INCF ottokar,F > und fertig. Solange es kein Ram gibt, daß selbstätig rechnen kann, muß auch der PIC den Wert aus dem Speicher holen, verändern, und wieder zurückschreiben. Oliver
Torben schrieb: > Was ist mit Takt gemeint? Etwa die Schrittgeschwindigkeit, also der > minimale zeitliche Abstand zwischen zwei Spannungspegeln? Wir reden aber schon von digitalen Prozessoren, nicht von Analogrechnern?
Torben schrieb: > W.S. schrieb: >> Das mag zwar sein, ist aber nicht bezeichnend für RISC. > > Martin S. schrieb: >> Der Geschwindigkeitsvorteil einer CPU kommt daher alleine durch's >> Pipelining und natuerlich durch die maximale Taktfrequenz zustande. > > Kann ich dann folgenden Schluss ziehen?: > > Prozessoren, die eher an der RISC-Philosophie angelegt sind, verfügen > über kurze elementare Befehle. Infolgedessen ist das Maschinenprogramm > länger, das z. B. der Compiler generieren würde. Daraus ergeben sich > dann auch viele Speicherzugriffe, weil jeder einzelne Assemblerbefehl > aus dem Hauptspeicher geholt werden muss. Diese Speicherzugriffe wirken > sich aber schlecht auf die Performance aus. Geheilt wird dieses Problem > durch Pipelining. Andererseits sind bei RISC alle Befehle gleichgroß, was das Laden selbiger vereinfacht. Bei CISC mit üblicherweise unterschiedlich langen Befehlen kann selbst das Laden eines einzigen Befehls viele Speicherzugriffe erfordern.
Da der kritische Pfad verkürzt wird, besteht natürlich ein Geschwindigkeitsvorteil gegenüber einer register memory architecture in der Gestalt einer höheren Taktrate. Transfers über den externen Bus sind wegen der Parasitären Kapazitäten am Interface relativ langsam. Diese Frage berührt das Prinzip der Entkopplung des Rechenwerkes vom Speicher. Insofern legt LoadStore auch die Grundlage für weitere Optimierungen wie Caches und Memory-Bursts.
W.S. schrieb: > So etwas setzt eine Harvard-Architektur voraus, wenn es effektiv > stattfinden soll. Denn damit können das Befehls-Holen und > Ergebnis-Speichern parallel zur gleichen Zeit stattfinden. Hauptsache mal wieder was geschrieben? Das hat nix mit CISC/RISC und Load-Store zu tun, sondern mit Pipelining. Ein RISC/CISC Prozessor ist auch als Multicycle baubar, aber dann eben lahm.
Torben schrieb: > Kann ich dann folgenden Schluss ziehen?: > > Prozessoren, die eher an der RISC-Philosophie angelegt sind, verfügen > über kurze elementare Befehle. Infolgedessen ist das Maschinenprogramm > länger, das z. B. der Compiler generieren würde. Daraus ergeben sich > dann auch viele Speicherzugriffe, weil jeder einzelne Assemblerbefehl > aus dem Hauptspeicher geholt werden muss. Diese Speicherzugriffe wirken > sich aber schlecht auf die Performance aus. Geheilt wird dieses Problem > durch Pipelining. Nee, das wuerde ich so nicht sagen. Was du ansprichst, ist die klassische FETCH-Stage. Das 'Problem' hat jede Architektur, die Stage kann voellig unterschiedlich ausfallen (und u.U auch Wartezyklen unterliegen). Sobald aber eine Programmsequenz im L1-RAM (am naechsten am CPU-Kern) vorliegt, ist das nahezu 'gratis' fuer die Performance. Im Silizium ist das eine Frage der Flaeche der Logikelemente (und wo man seinen Flaschenhals haben will). Oder, was man fuer Memory-Typen/Busse zur Verfuegung hat (Stichworte Harvard vs. von Neumann). Bei manchen Architekturen gibt's eine PREFETCH-stage, die z.b. gleich mehrere Instructionen holt, ev. sogar von unterschiedlichen Adressen (moeglicher Branch). Dann liegen die Instruktionen in (nicht exportierten) Registern. Die Pipeline heilt das Problem nur, wenn sie keine Pause einlegen muss (wenn besagter LOAD-Hazard eintritt: will X verrechnen, aber X noch nicht da). Ansonsten zu empfehlen: Die CS152-Kurse und viel PDF-Material zu MIPS-Architekturen von berkeley.edu. Es gibt auch RISC-Architekturen, die keine Registerbank besitzen (Stack-Maschinen) und bessere Code-Dichten aufweisen als i86, oder ARM Thumb. Schlussendlich sind fuer die Performance die genannten Faktoren (Wartezyklen, Taktfrequenz) und schliesslich dann die Anzahl der Befehle fuer eine gewisse Operation relevant. In der Praxis ist das mit RISC vs. CISC immer etwas verwirrend, die klassischen CISC haben oft eine starke Varianz in den Zyklen pro Befehl und weniger Pipelining, die klassischen RISC eher wenige Befehle und ~1-2 Z/B, dann gibt es aber auch neuere DSP-Hybriden, die man als CISC sehen wuerde, aber genau 1 Z/B benoetigen. Deswegen ist die Unterteilung sehr schwammig. Weniger Zyklenvarianz macht's einem Compiler sehr viel einfacher, zu optimieren, deswegen machen die RISC-Instruction-Sets fuer den Compilerbau schlicht weniger Arbeit. Philipp Klaus K. schrieb: > Andererseits sind bei RISC alle Befehle gleichgroß, was das Laden > selbiger vereinfacht. Das gilt laengst auch nicht mehr, siehe auch RISC-V 'compressed' oder Instruction-Sets wie von Xtensa.
Torben schrieb: > in diversen Büchern habe ich gelesen, dass RISC-Prozessoren eine > LOAD-STORE-Architektur zugrunde liegt. Papier ist geduldig. Und zeitlos. Schau mal auf das Datum der Veröffentlichung. > Das bedeutet nach meinem > Verständnis, dass - wenn im Assemblerprogramm in einer arithmetischen > Operation eine Variable verwendet wird, die sich noch im Hauptspeicher > befindet - auf diese nicht direkt zugegriffen werden kann, sondern, dass > sie erst mit dem LOAD-Befehl in eines der internen Register geschoben > werden muss, wo sie dann mit schnellen arithmetischen > Register-Register-Operationen weiterverarbeitet werden kann. Du verwendest da unangebrachte Adjektive wie "arithmetisch" oder "schnell". Tatsächlich bedeutet LOAD-STORE-Architektur nur, daß die einzigen Befehle, die auf externen Speicher (lies: alles was kein Register ist) zugreifen können, welche sind die Daten in Register laden (LOAD) bzw. Daten aus Registern sichern (STORE). Mehr nicht. > Ich frage mich, ob ich das richtig verstanden habe und ob daraus ein > Geschwindigkeitsvorteil resultiert. "LOAD-STORE-Architektur" ist ein beschreibender Begriff. Noch dazu einer, der eher wenig mit der alten RISC vs. CISC Debatte zu tun hat. Und die Frage nach einem Geschwindigkeitsvorteil ist auf dieser Ebene vollkommen fehl am Platz. Allerdings erscheint es mir offensichtlich, daß man einen Befehlssatz mit kürzeren Befehlen hinbekommt, wenn man sich bei der Mehrheit der Befehle auf Addressierungsarten beschränkt, die nur interne Register umfassen. Weil es davon sehr viel weniger gibt, als Speicherplätze im externen Adreßbereich. Weil deswegen viel weniger Adreßbits im Befehl gebraucht werden. Und auf der Ausführungsseite ist auch klar, daß Zugriffe auf Register schneller sind, als Zugriffe auf RAM. Trotzdem ist genauso klar, daß nur die wenigsten Programme mit den paar Hundert Bits auskommen, die in Registern gehalten werden können. Es wird also in jedem Fall Zugriffe auf externen Speicher geben müssen. Ob unter diesen Umständen eine CPU mit strikter LOAD-STORE-Architektur besser abschneidet als eine, die sagen wir mal indirekte Adressierung für alle Befehle anbietet, wäre noch zu untersuchen. Es wird auf jeden Fall auch vom Programm abhängen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.