Uma arquitetura pura de Harvard geralmente permite que um computador com um determinado nível de complexidade seja executado mais rapidamente do que uma arquitetura Von Neuman, desde que nenhum recurso precise ser compartilhado entre o código e as memórias de dados. Se as limitações de pinagem ou outros fatores obrigam o uso de um barramento para acessar os dois espaços de memória, essas vantagens tendem a ser amplamente anuladas.
Uma arquitetura Harvard "pura" será limitada à execução de código que é colocado na memória por algum mecanismo que não seja o processador que executará o código. Isso limita a utilidade dessas arquiteturas para dispositivos cuja finalidade não é definida pela fábrica (ou alguém com equipamento de programação especializado). Duas abordagens podem ser usadas para aliviar esse problema:
Alguns sistemas possuem áreas de código e memória separadas, mas fornecem hardware especial que pode ser solicitado a assumir brevemente o barramento de código, executar algumas operações e retornar o controle à CPU assim que essa operação estiver concluída. Alguns desses sistemas requerem um protocolo bastante elaborado para realizar essas operações, alguns possuem instruções especiais para executar essa tarefa e alguns até observam determinados endereços de "memória de dados" e acionam a aquisição / liberação quando é feita uma tentativa de acessá-los. . Um aspecto fundamental desses sistemas é que existem áreas de memória definidas explicitamente para "código" e "dados"; mesmo que seja possível para a CPU ler e gravar espaço em "código", ela ainda é reconhecida como sendo semanticamente diferente do espaço de dados. '
Uma abordagem alternativa usada em alguns sistemas de ponta é ter um controlador com dois barramentos de memória, um para código e outro para dados, ambos conectados a uma unidade de arbitragem de memória. Essa unidade, por sua vez, se conectou a vários subsistemas de memória usando um barramento de memória separado para cada um. Um acesso de código a um subsistema de memória pode ser processado simultaneamente com um acesso de dados a outro; somente se o código e os dados tentarem acessar o mesmo subsistema simultaneamente, um deles terá que esperar.
Em sistemas que usam essa abordagem, partes de um programa que não sejam críticas para o desempenho podem simplesmente ignorar os limites entre os subsistemas de memória. Se o código e os dados residirem no mesmo subsistema de memória, as coisas não serão executadas tão rapidamente como se estivessem em subsistemas separados, mas para muitas partes de um programa típico que não importam. Em um sistema típico, haverá uma pequena parte do código onde o desempenho realmente importa e funcionará apenas em uma pequena parte dos dados mantidos pelo sistema. Se alguém tivesse um sistema com 16K de RAM dividido em duas partições de 8K, seria possível usar instruções do vinculador para garantir que o código crítico de desempenho estivesse localizado próximo ao início do espaço de memória geral e os dados críticos de desempenho estivessem próximos ao fim. Se o tamanho geral do código aumentar para, por exemplo, 9K, o código no último 1K seria mais lento que o código colocado em outro lugar, mas esse código não seria crítico para o desempenho. Da mesma forma, se o código tivesse, por exemplo, apenas 6K, mas os dados aumentassem para 9K, o acesso ao 1K de dados mais baixo seria lento, mas se os dados críticos de desempenho estivessem localizados em outro lugar, isso não representaria um problema.
Observe que, embora o desempenho seja ideal se o código estiver abaixo de 8K e os dados estiverem abaixo de 8K, o design do sistema de memória mencionado acima não imporá nenhuma partição estrita entre código e espaço de dados. Se um programa precisar apenas de 1K de dados, o código poderá crescer até 15K. Se precisar apenas de 2K de código, os dados poderão aumentar para 14K. Muito mais versátil do que ter uma área de 8K apenas para código e uma área de 8K apenas para dados.