-
Beschreibung der parallelen Nutzung eines Heap-Speichers als Cache-Speicher
-
Die effiziente Steuerung von Informationsverarbeitungssystemen mit beschränkten Ressourcen, häufig bezeichnet als eingebettete (engl.: embedded) Systeme, mittels Programmcode, stellt in der Industrie ein bekanntes Problem dar. Auf solchen Systemen ist häufig unter anderem der flüchtige Speicher (RAM) aus Kostengründen stark begrenzt.
-
Bei der Erstellung von Programmcode zur Steuerung derartiger Speicherbeschränkter Informationsverarbeitungssystemene kommen prinzipiell drei Verwendungsarten des RAM-Speichers zum Einsatz: Stark-Speicher, Heap-Speicher und solcher, in dem globale Objekte abgelegt werden. Letztere Speicherbereiche zeichnen sich dadurch aus, dass sie während der gesammten Betriebszeit konzeptionell fest bestimmten Inhalten zugeordnet sind.
-
Der herkömmliche Ansatz zur Verwaltung des Speichers auf eingebetteten Systemen besteht darin, dabei ganz auf den Heap-Speicher zu verzichten. Dies sieht z. B. der ”Misra-C” Standard der Automobilindustrie vor. Der Hintergrund für diese Entscheidung ist sind folgende Bedenken: Eine Anforderung zur Reservierung eines Speicherbereichs im Heap-Speicher erfolgt in herkömmlichen Realisierungen einer Speicherverwaltung und den Programmabläufen typischerweise derart, dass die Erfüllbarkeit der Speicheranforderung bei der Konzeption des Systems nicht unter allen Bedingungen vorhergesagt werden kann. Dies ist im wesentlichen auf das Problem der Fragmentierung innerhalb des Heap-Speichers zurückzuführen. So besteht die Möglichkeit, dass an einem gewissen Punkt innerhalb des Programmablaufs eine Anforderung von einem Heap-Speicherbereich einer bestimmten Größe nicht erfüllt werden kann, obwohl absolut gesehen die entsprechende Menge an Speicher dort verfügbar ist, dies allerdings nicht zusammenhängend, d. h. nicht in einer linear angsteigenden Folge von Adresswerten der entsprechenden Speicherzellen. Stattdessen liegen diese in einem solchen Fall in verschiedenen freien Blöcken, jeweils getrennt durch bereits reservierte Blöcke, im Heap-Speicher vor.
-
Aus diesem Grund verzichtet man in Informationsverarbeitungssystemen mit stark begrenztem RAM häufig ganz auf die Verwendung eines Heap-Speichers. Stattdessen nimmt man alle Speicherallokationen auf dem sogenannten Stack-Speicher vor, in welchem sich das Problem aufgrund seiner Beschaffenheit nicht stellt. Dieser Speicherbereich ist ohnehin in solchen Systemen immer notwendig: Im Programmcode wird er verwendet, um Zwischenwerte, die temporär nicht in den Registern der Zentralrecheneinheit gehalten werden können, auszulagern. Ferner muss jede von der Hauptroutine angesprungene Unterroutine die jeweilige Rücksprungadresse, an welcher die aufrufende Routine nach Beendigung der gerade aktiven Routine fortzusetzen ist, sowie ggf. weitere Informationen, im Stack-Speicher ablegen.
-
Eine weitere herkömmliche Art der Speichernutzung ist die Verwendung als Cache-Speicher, wobei wir hier von einem Cache-Speicher sprechen, der von dem Programmcode gesteuert wird, der also nicht zu verwechseln ist mit dem Cache-Speicher über den eventuell die Zentralrecheneinheit selbst verfügt (Hardware-Cache). Ein solcher Cache-Speicherbereich ist auch im RAM angesiedelt und hat die Aufgabe, Daten vorzuhalten, die eine effizientere Programmabarbeitung ermöglichen als in dem Fall, in dem sie nicht im Cache gehalten werden. Somit ist die Nutzung des RAM als Cache-Speicher eine opportunistische: wann immer möglich (d. h., der entsprechende Speicher darf zum jeweiligen Zeitpunkt nicht für andere Zwecke reserviert sein) und nützlich (d. h. die im Cache vorzuhaltenden Daten müssen potentiell eine effizientere Programmabarbeitung ermöglichen), werden Cache-Inhalte gespeichert. Gleichzeitig besteht die Möglichkeit, jeden solchen zur Vorhaltung eine Cache-Inhalts reservierten Cache-Speicherbereichs jederzeit innerhalb des Programmablaufs auch wieder freizugeben. Dies geschieht in einem Cache-Speicherbereich, der rein für die Vorhaltung von Cache-Inhalten verwendet wird, immer dann, wenn ein anderer Cache-Inhalt an die entsprechende Stelle geschrieben wird. Wann ein Cache-Inhalt durch einen anderen ersetzt wird, entscheidet die Cache-Speicherverwaltungseinheit, welche sich typischerweise aus der Kombination eines internen Zustands und einer sogenannten Ersetzungsstrategie zusammensetzt. Eine häufige Ersetzungsstrategie ist ”least-recently-used” (LRU), zu deutsch etwa ”am längsten zurückliegend genutzt”. Bei dieser Strategie wird in dem Fall, dass keine freien Cache-Speicherbereiche existieren, bei der Reservierung eines neuen Cache-Inhalts derjenige Eintrag gelöscht, welcher am längsten zurückliegend genutzt wurde.
-
Die Nachteile der oben skizzierten bisherigen Ansätze sind die Folgenden: Im Falle der Vermeidung der Heap-Nutzung muss für den Stack-Speicher ein wesentlich größerer Bereich vorgesehen werden, da alle Speicherbereiche, die während der Programmabarbeitung reserviert werden, dort lokalisiert sind. Der gesamte als Stack-Speicher eingeteilte Speicherbereich kann nicht anderweitig genutzt werden – etwa als Cache. Im Falle der Heap-Nutzung besteht im herkömmlichen Ansatz der Heap-Speicherverwaltung das Problem der Fragmentierung des Heap-Speichers.
-
Das Problem wird gelöst durch ein Speicherverwaltungsverfahren nach Anspruch 1 und ein Informationsverarbeitungssystem nach Anspruch 7. Dadurch wird das Effizienzproblem vermieden, welches entsteht, wenn man statt des Heap-Speichers nur Stack-Speicher verwendet und gleichzeitig vom Einsatz von Cache-Speicher profitieren könnte: Der gesamte Stack-Speicherbereich steht zu jedem Zeitpunkt im Programmablauf nur für die Nutzung als Stack-Speicherbereich zur Verfügung, unabhängig davon, wieviel Speicherbereich darin tatsächlich zum jeweiligen Zeitpunkt zu diesem Zwecke darin reserviert ist. Dies liegt daran, dass durch die im Programmcode implizite Reservierung von Stack-Speicher durch entsprechende Instruktionen, die von der Zentralrecheneinheit verarbeitet werden, eine explizite Verwaltung des Stack-Speicherbereichs durch den Programmcode selbst nicht möglich ist: Wenn eine Routine durch die Reservierung eines weiteren Bereichs innerhalb des Stack-Speichers in einen Speicherbereich vordringen würde, der gerade einen Cache-Inhalt enthält, so würde dieser einfach überschrieben werden, ohne dass es möglich wäre, den internen Zustand der Cache-Speicherverwaltung zu aktualisieren oder vor der Freigabe des Cache-Inhalts Operationen einzuleiten, die je nach der Art der Verwendung des Cache-Speichers notwendig sein können.
-
Für die Verwendung eines Cache-Speichers kommen alle Anwendungen in Betracht, bei denen durch das Vorhalten von Daten im Cache-Speicher spätere Operationen beschleunigt werden. Die wesentlichen Operationen, die hier relevant sind, sind Berechnungen, bei denen auf im Cache vorgehaltene Vorberechnungen zurückgegriffen wird, und Schreibzugriffe auf im Vergleich zum RAM durch deutlich langsamere Zugriffszeiten ausgezeichnete Speichermedien.
-
Die Erfindung ist im wesentlichen geeignet für solche Geräte, welche kein Betriebssystem mit eigener Speicherverwaltung betreiben. Als Beispiel für ein solches Betriebssystem sei Linux genannt, welches auch in Varianten speziell für ressourcenbeschränkte Plattformen existiert (”embedded Linux”). Ein solches Betriebssystem hat eigene Mechanismen, welche einen ähnlichen Erfolg erzielen wie die vorliegende Erfindung, ohne dass im Code des ausgeführten Programms überhaupt besondere Vorkehrungen getroffen werden müssten. Die entsprechenden Technologien, wie sie von solchen Betriebssystemen eingesetzt werden, basieren jedoch auf komplexen Speicherverwaltungsmechanismen und sind mit besonderen Anforderungen an die verwendete Zentralrecheneinheit verbunden: diese muss zur Abbildung von virtuellen Adressen auf physische über eine sogenannte Memory Management Unit (MMU) verfügen. Ferner setzt die Verwendung eines Betriebssystems mit derartiger Speicherverwaltung Systemressourcen an RAM, nicht-flüchtigem Speicher sowie eine Rechenkapazität voraus, die weit über denen von stark ressourcenbeschränkten Systemen liegen. Die vorliegende Erfindung ist gerade für solche Systeme von Vorteil, auf denen die Verwendung eines derartigen Betriebssystems nicht in Frage kommt. Sie stellt keine keine besonderen Anforderungen an die Hardware. Alle Aspekte der Erfindung lassen sich rein in Software realisieren. Dies ist von hoher Bedeutung, da nur die Verwendung von universellen Hardwareelementen, die in hohen Stückzahlen produziert werden, kostensparende Lösungen erlaubt.
-
Die Erfindung betrifft sowohl eine Vorrichtung als auch ein Verfahren zur effizienten Verwendung von RAM Speicher: ersteres, weil im Rahmen der Erfindung eine Speicherverwaltungseinheit mit bestimmten Eigenschaften realisiert wird, und letzteres, weil im zur Ausführung gelangenden Programmcode selbst auch Vorkehrungen zur Verwendung der Erfindung vorgenommen werden müssen.
-
Wir geben im Folgenden ein Beispiel an, welches den Nutzen der Erfindung demonstriert. Dazu werden wir zunächst einen Programmablauf zuerst nach dem herkömmlichen Ansatz und anschließend den analogen Ablauf unter Verwendung der Erfindung darstellen. Dies geschieht im Zusammenhang mit der Verwendung eines Cache, welcher Speicherinhalte eines nicht-flüchtigen Speichermediums vorhält, welches sich durch deutlich langsamere Zeit für Schreibzugriffe im Vergleich mit dem RAM auszeichnet. Dabei verstehen wir unter den nicht-flüchtigen Speicherbereichen sogenannte Speicherseiten, welche eine von der technischen Hardwarelösung abhängige Größe haben und die Eigenschaft haben, nur als Ganzes beschreibbar zu sein. Dabei ist die Dauer eines solchen Schreibzugriffs unabhängig von der Anzahl der tatsächlich geänderten Datenbits innnerhalb der Speicherseite, und ist typischerweise um einen Faktor von tausend oder mehr zeitintensiver als ein Schreibzugriff auf eine RAM-Speicherzelle. Aus diesem Grund werden wir uns bei der nachfolgenden Beschreibung des beispielhaften Programmablaufs auf die für die Erfindung maßgeblichen Aspekte beschränken; dies sind die Speicherreservierungen im RAM und die Schreibzugriffe im nicht-flüchtigen Speicher.
-
In der Zeichnung ist mit 1 eine Darstellung der herkömmlichen Verwendung des RAM Speichers als Stack-Speicher, zusammengesetzt aus der den Einzelbereichen 6, 7 und 8 und Speicher für Speicherbereiche mit beliebig langer Gültigkeitsdauer, zusammengesetzt aus den beiden Einzelbereichen 9 und 10. In diesem Beispiel ist der Gesamte verfügbare flüchtige Speicher des Informationsverarbeitungssystems in diese beiden Bereich geteilt. Zu dem Speicherbereich mit beliebig langer Gültigkeitsdauer ist anzumerken, dass dieser im vorliegenden Beispiel, in dem kein Heap-Speicherbereich verwendet wird, für die gesamte Dauer des Programmablaufs für einen bestimmten Inhalt reserviert ist, unabhängig davon, wann zum ersten Mal ein Schreibzugriff darin erfolgt. Ferner ist, mit 2 bezeichnet, schematisch ein nicht-flüchtiger Speicherbereich des gleichen Informationsverarbeitungssystems dargestellt. Aus reinen Darstellungsgründen hat er in der Zeichnung die gleiche Größe wie der RAM, in tatsächlichen Systemen hat er üblicherweise eine abweichende Größe. Nehmen wir nun den nachfolgend beschriebenen Programmablauf an. Dabei ignorieren wir, wenn wir von der Reservierung von Speicherbereichen auf dem Stack sprechen, die weiter oben erwähnten, typischerweise kleinen Speicherbereiche, die dort zur Ablage von Rücksprungadressen und zur Auslagerung von Registerwerten aus der Zentralrecheneinheit verwendet werden. Stattdessen verstehen wir unter Reservierungen von Speicherbereichen solche von beträchtlicher Größe, wie sie beispielsweise zu komplexen mathematischen Berechnungen nötig sind oder für das Speichern von längeren Nachrichten, die das System über entsprechende Schnittstellen empfängt oder sendet. Nun folgt der abstrahierte, exemplarische Programmablauf, wie er mittels des herkömllichen Ansatzes erfolgt.
- • Zunächst wird die Routine A ausgeführt. Sie reserviert in dem Stack-Speicherbereich den Unterbereich 6. Ferner führt sie einen Schreibzugriff auf den Speicherbereich 11 im nicht-flüchtigen Speicher durch. Anschließend ruft sie die Routine B auf.
- • Die Routine B reserviert in dem Stack-Speicherbereich den Unterbereich 7. Ferner führt sie einen Schreibzugriff auf den Speicherbereich 12 im nicht-flüchtigen Speicherbereich durch. Anschließend ruft sie die Routine C auf.
- • Die Routine C reserviert auf dem Stack-Speicherbereich den Unterbereich 8 und führt dann einen Schreibzugriff in den Bereich 11 des nicht-flüchtigen Speichers durch, dann schreibt sie erstmals Daten in den Unterbereich 10 des dauerhaften RAM-Speicherbereichs. Dann kehrt die Routine zurück zur Routine B.
- • Die Routine B führt jeweils einen Schreibzugriff in dem Speicherbereich 11 und 12 des nicht-flüchtigen Speichers durch. Anschließend schreibt sie erstmals Daten in den Unterbereich 9 des dauerhaften RAM-Speicherbereichs. Danach kehrt sie zur Routine A zurück.
- • Die Routine A führt zunächst einen Schreibzugriff auf den Unterbereich 11 und dann den Unterbereich 12 des nicht-flüchtigen Speichers durch.
-
Bei diesem Ablauf wurde insgesamt sieben Schreibzugriffe auf Bereiche im nicht-flüchtigen Speichermedium durchgeführt.
-
Nun betrachten wir den gleichen Ablauf, wie er unter Verwendung der Erfindung gestaltet werden kann. Dazu finden sich in der Zeichnung wiederum die Darstellung des RAM-Speichers, zusammengesetzt aus Stack-Speicher (13), sowie den in diesem Fall neu dazugekommenen Heap-Speicher, der sich aus den restlichen Bereichen des RAMs (14, 15, 16, 17 und 18) zusammensetzt. Der Stack-Speicher kann in diesem Fall viel kleiner sein, da er hier nur noch zur Speicherung der Rücksprungadressen, ggf. weiterer Verwaltungsinformationen und ausgelagerten Registerwerten verwendet wird. Die größeren Speicherbereiche, welche die einzelnen Routinen reservieren, werden nun in dem temporären Heap-Speicher vorgenommen, der sich aus den Einzelbereichen 14, 15 und 16 zusammensetzt. Der dauerhafte Heap-Speicher mit den Einzelbereichen 17 und 18 ersetzt die fest zugeteilten dauerhaften Speicherbereiche 9 und 10 aus dem vorhergehenden Beispiel.
-
Im Folgenden stellen wir den gleichen Programmablauf wie im vorhergehenden Beispiel dar, mit den einzigen Unterschieden, dass
- • nun die temporären Reservierungen von Speicherbereichen durch einzelne Routinen nicht mehr in dem Stack-Speicher, sondern in dem temporären Heap-Speicher vorgenommen werden,
- • die Speicherbereiche im dauerhaften Heap-Speicher nicht mehr über den gesamten Programmablauf reserviert sind, sondern nur ab dem Zeitpunkt, ab dem diese benötigt werden, bis zu dem Zeitpunkt, ab dem sie nicht mehr benötigt werden,
- • solche Heap-Speicherbereiche potentiell als Cache-Speicher für Schreibzugriffe im nicht-flüchtigen Speichermedium genutzt werden können, die zum jeweiligen Zeitpunkt nicht reserviert sind. Die solchermaßen parallele Verwendung des Heap-Speichers als Cache ist in der Zeichnung dargestellt unter 4. Jeder der dort eingezeichneten einzelnen Cache-Teilbereiche 19, 20, 21 und 22 kann einen Speicherbereich aus dem nicht-flüchtigen Speichermedium vorhalten. Letzteres ist für dieses Beispiel unter 5 mit seinen Teilbereichen 23 und 24 dargestellt.
-
Nun geben wir den analogen Programmablauf unter Verwendung der Erfindung an:
- • Zunächst wird die Routine A ausgeführt. Sie reserviert in dem temporären Heap-Speicher den Unterbereich 14. Ferner führt sie über den Cache-Speicher einen Schreibzugriff auf den Speicherbereich 23 im nicht-flüchtigen Speicher durch. Dabei wird von der Speicherverwaltungseinheit der Speicherbereich 23 aus dem nicht-flüchtigen Speicher in den Cache-Speicherbereich 20 geladen, dort wird der Schreibzugriff vollzogen, ein Schreibzugriff auf den tatsächlichen Speicherbereich 23 des nicht-flüchtigen Speichermediums findet nicht statt. Anschließend ruft die aktuelle Routine die Routine B auf.
- • Die Routine B reserviert in dem temporären Heap-Speicher den Unterbereich 15. Ferner führt sie über den Cache-Speicher einen Schreibzugriff auf den Speicherbereich 24 im nicht-flüchtigen Speicherbereich durch. Dabei wird von der Speicherverwaltungseinheit der Speicherbereich 24 aus dem nicht-flüchtigen Speicher in den Cache-Speicherbereich 21 geladen, dort wird der Schreibzugriff vollzogen, ein Schreibzugriff auf den tatsächlichen Speicherbereich 24 des nicht-flüchtigen Speichermediums findet nicht statt. Anschließend ruft sie die Routine C auf.
- • Die Routine C reserviert auf dem Stack-Speicherbereich den Unterbereich 16. Dabei veranlaßt die Speicherverwaltungseinheit zunächst, dass der Speicherinhalt 20 des Cache-Speichers in den den Bereich 22 des des als Cache-Speichers nutzbaren dauerhaften Heap-Speicher kopiert wird. Erst anschließend führt sie die Reservierung des Speicherbereichs 16 durch. Dann führt sie über den Cache einen Schreibzugriff in den Bereich 23 des nicht-flüchtigen Speichers durch. Dabei wird der Cache-Speicherbereich 22 beschrieben, ein Schreibzugriff auf den tatsächlichen Speicherbereich 23 des nicht-flüchtigen Speichermediums findet nicht statt. Dann reserviert sie den Unterbereich 18 des dauerhaften RAM-Speicherbereichs und beschreibt ihn mit Daten. Dann gibt die Routine C den Bereich 16 im temporären Heap-Speicher wieder frei und kehrt zurück zur Routine B.
- • Die Routine B führt über den Cache jeweils einen Schreibzugriff in dem Speicherbereich 23 und 24 des nicht-flüchtigen Speichers durch. Dabei werden tatsächlich die Bereiche 22 und 21 des Cache-Speichers beschrieben, Schreibzugriffe auf die tatsächlichen Speicherbereiche 23 und 24 des nicht-flüchtigen Speichermediums finden nicht statt. Anschließend reserviert sie den Unterbereich 17 des dauerhaften Heap-Speichers. Dabei wird von der Speicherverwaltungseinheit zunächst innerhalb des Cache-Speichers der Bereich 22 in den Bereich 20 und der Bereich 21 in den Bereich 19 kopiert, bevor sie die Reservierung des Bereichs 17 im dauerhaften Heap-Speicher vornimmt. Danach gibt die Routine B den Speicherbereich 15 im temporären Heap-Speicher wieder frei und kehrt zur Routine A zurück.
- • Die Routine A führt zunächst einen Schreibzugriff auf den Unterbereich 23 und dann den Unterbereich 24 des nicht-flüchtigen Speichers über den Cache-Speicher durch. Dabei werden zunächst die entprechenden Schreibzugriffe in den Speicherbereichen 20 und 19 des Cache-Speichers vollzogen. Anschließend veranlasst die Routine A, dass alle Inhalte des Cache-Speichers in den nicht-flüchtigen Speicher übertragen werden. Daraufhin vollzieht die Speicherverwaltungseinheit des Schreiben des vollständigen Inhalts des Bereichs 19 des Cache-Speichers in den Bereich 24 und des Bereichs 20 des Cache-Speichers in den Bereich 23 des nicht-flüchtigen Speichers.
-
In diesem Beispiel werden unter Verwendung der Erfindung insgesamt nur zwei Schreibzugriffe in Bereiche des nicht-flüchtigen Speichers getätigt. Dies stellt einen erheblichen Vorteil zu den sieben Schreibzugriffen aus dem vorhergehenden Beispiel nach herkömmlichen Verfahren dar.