Una pura architettura di Harvard generalmente consentirà a un computer con un determinato livello di complessità di funzionare più velocemente di un'architettura Von Neuman, a condizione che non sia necessario condividere risorse tra il codice e le memorie di dati. Se le limitazioni della piedinatura o altri fattori obbligano l'uso di un bus per accedere a entrambi gli spazi di memoria, tali vantaggi potrebbero essere ampiamente annullati.
Un'architettura "pura" di Harvard sarà limitata all'esecuzione del codice che viene messo in memoria da un meccanismo diverso dal processore che eseguirà il codice. Ciò limita l'utilità di tali architetture per dispositivi il cui scopo non è impostato dalla fabbrica (o da qualcuno con apparecchiature di programmazione specializzate). È possibile utilizzare due approcci per alleviare questo problema:
Alcuni sistemi hanno aree di memoria e di codice separate, ma forniscono hardware speciale che può essere richiesto di prendere brevemente il bus di codice, eseguire alcune operazioni e restituire il controllo alla CPU una volta completata tale operazione. Alcuni di questi sistemi richiedono un protocollo abbastanza elaborato per eseguire tali operazioni, alcuni hanno istruzioni speciali per eseguire tale compito e alcuni addirittura controllano determinati indirizzi di "memoria dati" e attivano l'acquisizione / rilascio quando si tenta di accedervi . Un aspetto chiave di tali sistemi è che esistono aree di memoria esplicitamente definite per "codice" e "dati"; anche se è possibile per la CPU leggere e scrivere lo spazio "codice", viene comunque riconosciuto come semanticamente diverso dallo spazio dati ".
Un approccio alternativo che viene utilizzato in alcuni sistemi di fascia alta è quello di disporre di un controller con due bus di memoria, uno per il codice e uno per i dati, entrambi collegati a un'unità di arbitrato di memoria. A sua volta quell'unità si è connessa a vari sottosistemi di memoria usando un bus di memoria separato per ciascuno. Un accesso al codice a un sottosistema di memoria può essere elaborato contemporaneamente con un accesso ai dati a un altro; solo se codice e dati tentano di accedere contemporaneamente allo stesso sottosistema, uno dovrà attendere.
Sui sistemi che utilizzano questo approccio, le parti non critiche per le prestazioni di un programma possono semplicemente ignorare i confini tra i sottosistemi di memoria. Se il codice e i dati risiedono nello stesso sottosistema di memoria, le cose non funzioneranno velocemente come se fossero in sottosistemi separati, ma per molte parti di un programma tipico che non contano. In un sistema tipico, ci sarà una piccola parte del codice in cui le prestazioni contano davvero, e funzionerà solo su una piccola parte dei dati detenuti dal sistema. Se uno avesse un sistema con 16K di RAM che era diviso in due partizioni 8K, si potevano usare le istruzioni del linker per assicurarsi che il codice critico per le prestazioni si trovasse vicino all'inizio dello spazio di memoria complessivo e che i dati critici per le prestazioni fossero vicini al fine. Se la dimensione complessiva del codice aumenta, ad esempio 9K, il codice all'interno dell'ultimo 1K verrebbe eseguito più lentamente del codice inserito altrove, ma tale codice non sarebbe critico per le prestazioni. Allo stesso modo, se il codice fosse ad esempio solo 6K, ma i dati aumentassero a 9K, l'accesso all'1K di dati più basso sarebbe lento, ma se i dati critici per le prestazioni fossero posizionati altrove, ciò non costituirebbe un problema.
Si noti che mentre le prestazioni sarebbero ottimali se il codice fosse inferiore a 8K e i dati fossero inferiori a 8K, la progettazione del sistema di memoria di cui sopra non imporrebbe alcuna rigida partizione tra il codice e lo spazio dati. Se un programma necessita solo di 1K di dati, il codice potrebbe aumentare fino a 15K. Se necessita solo di 2 KB di codice, i dati potrebbero aumentare a 14 KB. Molto più versatile di avere un'area 8K solo per il codice e un'area 8K solo per i dati.