Una arquitectura pura de Harvard generalmente permitirá que una computadora con un determinado nivel de complejidad funcione más rápido que una arquitectura de Von Neuman, siempre que no sea necesario compartir recursos entre el código y las memorias de datos. Si las limitaciones de pines u otros factores obligan al uso de un bus para acceder a ambos espacios de memoria, es probable que tales ventajas se anulen en gran medida.
Una arquitectura "pura" de Harvard se limitará a ejecutar código que se guarda en la memoria mediante algún mecanismo que no sea el procesador que ejecutará el código. Esto limita la utilidad de tales arquitecturas para dispositivos cuyo propósito no es establecido por la fábrica (o alguien con equipo de programación especializado). Se pueden usar dos enfoques para aliviar este problema:
Algunos sistemas tienen áreas separadas de código y memoria, pero proporcionan hardware especial al que se le puede pedir que se haga cargo brevemente del bus de código, realice algunas operaciones y devuelva el control a la CPU una vez que se complete dicha operación. Algunos de estos sistemas requieren un protocolo bastante elaborado para llevar a cabo tales operaciones, algunos tienen instrucciones especiales para realizar dicha tarea, y algunos incluso observan ciertas direcciones de "memoria de datos" y activan la toma / liberación cuando se intenta acceder a ellas. . Un aspecto clave de tales sistemas es que existen áreas de memoria explícitamente definidas para "código" y "datos"; incluso si es posible que la CPU lea y escriba el espacio de "código", todavía se reconoce que es semánticamente diferente del espacio de datos '.
Un enfoque alternativo que se utiliza en algunos sistemas de gama alta es tener un controlador con dos buses de memoria, uno para código y otro para datos, que se conectan a una unidad de arbitraje de memoria. Esa unidad a su vez se conectó a varios subsistemas de memoria utilizando un bus de memoria separado para cada uno. Un código de acceso a un subsistema de memoria puede procesarse simultáneamente con un acceso de datos a otro; solo si el código y los datos intentan acceder al mismo subsistema simultáneamente, uno tendrá que esperar.
En los sistemas que utilizan este enfoque, las partes de un programa que no son críticas para el rendimiento pueden simplemente ignorar los límites entre los subsistemas de memoria. Si el código y los datos residen en el mismo subsistema de memoria, las cosas no se ejecutarán tan rápido como si estuvieran en subsistemas separados, sino para muchas partes de un programa típico que no importará. En un sistema típico, habrá una pequeña parte del código donde el rendimiento realmente importa, y solo operará en una pequeña porción de los datos que posee el sistema. Si uno tuviera un sistema con 16K de RAM que se dividiera en dos particiones de 8K, uno podría usar las instrucciones del enlazador para asegurarse de que el código crítico para el rendimiento se ubicara cerca del inicio del espacio de memoria general, y los datos críticos para el rendimiento estuvieran cerca del final. Si el tamaño general del código crece, por ejemplo, a 9K, el código dentro del último 1K se ejecutaría más lentamente que el código colocado en otro lugar, pero ese código no sería crítico para el rendimiento. Del mismo modo, si el código fuera, por ejemplo, solo 6K, pero los datos crecieran a 9K, el acceso al 1K más bajo de datos sería lento, pero si los datos críticos para el rendimiento estuvieran ubicados en otro lugar, eso no representaría un problema.
Tenga en cuenta que si bien el rendimiento sería óptimo si el código estuviera por debajo de 8K y los datos estuvieran por debajo de 8K, el diseño del sistema de memoria mencionado anteriormente no impondría ninguna partición estricta entre el código y el espacio de datos. Si un programa solo necesita 1K de datos, el código podría crecer hasta 15K. Si solo necesita 2K de código, los datos podrían crecer hasta 14K. Mucho más versátil que tener un área de 8K solo para código y un área de 8K solo para datos.