Mit einer reinen Harvard-Architektur kann ein Computer mit einer bestimmten Komplexität im Allgemeinen schneller ausgeführt werden als mit einer Von Neuman-Architektur, vorausgesetzt, dass keine Ressourcen zwischen dem Code und den Datenspeichern geteilt werden müssen. Wenn Pinbelegungsbeschränkungen oder andere Faktoren die Verwendung eines Busses zwingen, um auf beide Speicherbereiche zuzugreifen, können solche Vorteile weitgehend zunichte gemacht werden.
Eine "reine" Harvard-Architektur beschränkt sich darauf, Code auszuführen, der durch einen anderen Mechanismus als den Prozessor, der den Code ausführt, in den Speicher gestellt wird. Dies schränkt den Nutzen solcher Architekturen für Geräte ein, deren Zweck nicht vom Werk festgelegt wurde (oder für Geräte mit speziellen Programmiergeräten). Zwei Ansätze können verwendet werden, um dieses Problem zu beheben:
Einige Systeme verfügen über separate Code- und Speicherbereiche, bieten jedoch spezielle Hardware, die aufgefordert werden kann, kurz den Codebus zu übernehmen, eine Operation auszuführen und die Steuerung an die CPU zurückzugeben, sobald eine solche Operation abgeschlossen ist. Einige solcher Systeme erfordern ein ziemlich ausgefeiltes Protokoll, um solche Operationen auszuführen, andere haben spezielle Anweisungen, um eine solche Aufgabe auszuführen, und einige überwachen sogar bestimmte "Datenspeicher" -Adressen und lösen die Übernahme / Freigabe aus, wenn versucht wird, auf sie zuzugreifen . Ein Schlüsselaspekt solcher Systeme ist, dass es explizit definierte Speicherbereiche für "Code" und "Daten" gibt; Selbst wenn es der CPU möglich ist, "Code" -Raum zu lesen und zu schreiben, wird sie immer noch als semantisch anders als der Datenraum erkannt. '
Ein alternativer Ansatz, der in einigen High-End-Systemen verwendet wird, besteht darin, eine Steuerung mit zwei Speicherbussen zu haben, einen für Code und einen für Daten, die beide mit einer Speicherarbitrierungseinheit verbunden sind. Diese Einheit wiederum ist über jeweils einen separaten Speicherbus mit verschiedenen Speichersubsystemen verbunden. Ein Codezugriff auf ein Speichersubsystem kann gleichzeitig mit einem Datenzugriff auf ein anderes verarbeitet werden; Nur wenn Code und Daten versuchen, gleichzeitig auf dasselbe Subsystem zuzugreifen, muss einer der beiden warten.
Auf Systemen, die diesen Ansatz verwenden, können nicht leistungskritische Teile eines Programms die Grenzen zwischen Speichersubsystemen einfach ignorieren. Wenn sich der Code und die Daten zufällig im selben Speichersubsystem befinden, laufen die Dinge nicht so schnell wie in separaten Subsystemen, aber für viele Teile eines typischen Programms spielt das keine Rolle. In einem typischen System gibt es einen kleinen Teil des Codes, auf den es wirklich ankommt, und er wird nur auf einen kleinen Teil der vom System gespeicherten Daten angewendet. Wenn ein System mit 16 KB RAM in zwei 8-KB-Partitionen unterteilt ist, kann mithilfe von Linkeranweisungen sichergestellt werden, dass sich der leistungskritische Code am Anfang des gesamten Speicherbereichs befindet und die leistungskritischen Daten sich in der Nähe des befinden Ende. Wenn die Gesamtcodegröße auf z. B. 9 KB ansteigt, Code innerhalb der letzten 1 KB wird langsamer ausgeführt als an anderer Stelle platzierter Code, dieser Code ist jedoch nicht leistungskritisch. Wenn der Code beispielsweise nur 6 KB groß wäre, die Daten jedoch auf 9 KB anwachsen würden, wäre der Zugriff auf die niedrigsten 1 KB langsam. Wenn sich die leistungskritischen Daten jedoch an einer anderen Stelle befinden würden, wäre dies kein Problem.
Es ist zu beachten, dass, während die Leistung optimal wäre, wenn der Code unter 8 K und die Daten unter 8 K wären, der oben erwähnte Entwurf des Speichersystems keine strikte Aufteilung zwischen Code und Datenraum auferlegen würde. Wenn ein Programm nur 1 KB Daten benötigt, kann der Code auf 15 KB anwachsen. Wenn nur 2 KB Code benötigt werden, können die Daten auf 14 KB anwachsen. Viel vielseitiger als ein 8K-Bereich nur für Code und ein 8K-Bereich nur für Daten.