¿Cómo acceden los procesadores a la memoria? Aprenda más sobre las estructuras en lenguaje C y cómo usarlas.
Este artículo explicará primero el concepto de granularidad de acceso a la memoria para que podamos desarrollar una comprensión básica de cómo un procesador accede a la memoria. Luego, analizaremos más de cerca el concepto de alineación de datos e investigaremos el diseño de la memoria para ver algunas estructuras de ejemplo.
En un artículo anterior sobre estructuras en C incrustada, observamos que reorganizar el orden de los miembros en una estructura puede cambiar la cantidad de memoria necesaria para almacenar una estructura. También vimos que un compilador tiene ciertas restricciones al asignar memoria a los miembros de una estructura. Estas restricciones, denominadas requisitos de alineación de datos, permiten que el procesador acceda a las variables de manera más eficiente al costo de un espacio desperdiciado (conocido como "relleno”) Que puede aparecer en el diseño de la memoria.
Este artículo explicará primero el concepto de granularidad de acceso a la memoria para que podamos desarrollar una comprensión básica de cómo un procesador accede a la memoria. Luego, analizaremos más de cerca el concepto de alineación de datos e investigaremos el diseño de la memoria para ver algunas estructuras de ejemplo.
Vale la pena mencionar que el sistema de memoria de una computadora puede ser mucho más complicado de lo que se presenta aquí. El objetivo de este artículo es discutir algunos conceptos básicos que pueden ser útiles al programar un sistema integrado.
Por lo general, visualizamos la memoria como una colección de ubicaciones de almacenamiento de un solo byte como se muestra en la Figura 1. Cada una de estas ubicaciones tiene una dirección única que nos permite acceder a los datos de esa dirección.
Sin embargo, un procesador generalmente accede a la memoria en fragmentos de más de un byte. Por ejemplo, un procesador puede acceder a la memoria en trozos de cuatro bytes. En este caso, podemos visualizar los 12 bytes consecutivos de la Figura 1 como se muestra en la Figura 2 a continuación.
Quizás se pregunte cuál es la diferencia entre estas dos formas de manejar la memoria. Con la Figura 1, el procesador lee y escribe en la memoria un byte a la vez. Tenga en cuenta que, antes de leer una ubicación de la memoria o escribir en ella, necesitamos acceder a esa celda de memoria, y cada acceso a la memoria lleva algún tiempo. Supongamos que queremos leer los primeros ocho bytes de la memoria en la Figura 1. Para cada byte, el procesador debe acceder a la memoria y leerla. Por lo tanto, para leer el contenido de los primeros ocho bytes, el procesador tendrá que acceder a la memoria ocho veces.
Con la Figura 2, el procesador lee y escribe en la memoria cuatro bytes a la vez. Por lo tanto, para leer los primeros cuatro bytes, el procesador accede a la dirección 0 de la memoria y lee las cuatro ubicaciones de almacenamiento consecutivas (dirección 0 a 3). De manera similar, para leer el siguiente fragmento de cuatro bytes, el procesador necesita acceder a la memoria una vez más. Va a la dirección 4 y lee las ubicaciones de almacenamiento de las direcciones 4 a 7 simultáneamente. Con trozos de tamaño byte, se requieren ocho accesos de memoria para leer los ocho bytes consecutivos de memoria. Sin embargo, con la Figura 2, solo se requieren dos accesos de memoria. Como se mencionó anteriormente, cada acceso a la memoria toma algún tiempo. Dado que la configuración de la memoria que se muestra en la Figura 2 reduce el número de accesos, puede llevar a una mayor eficiencia de procesamiento.
El tamaño de los datos que utiliza un procesador al acceder a la memoria se denomina granularidad de acceso a la memoria. La Figura 2 muestra un sistema con granularidad de acceso a memoria de cuatro bytes.
Existe otra técnica importante que los diseñadores de hardware emplean a menudo para hacer que un sistema de procesamiento sea más eficiente: restringen el procesador para que pueda acceder a la memoria solo en ciertos límites. Por ejemplo, un procesador puede acceder a la memoria de la Figura 2 solo en los límites de cuatro bytes, como se muestra en las flechas rojas de la Figura 3.
¿Esta limitación de límites hará que el sistema sea significativamente más eficiente? Miremos más de cerca. Supongamos que necesitamos leer el contenido de las ubicaciones de la memoria con las direcciones 3 y 4 (indicadas por los rectángulos verde y azul en la Figura 3). Si el procesador pudiera leer un fragmento de cuatro bytes a partir de una dirección arbitraria, podríamos acceder a la dirección 3 y leer las dos ubicaciones de memoria deseadas con un solo acceso de memoria. Sin embargo, como se mencionó anteriormente, el procesador no puede acceder directamente a una dirección arbitraria; más bien, accede a la memoria solo en ciertos límites. Entonces, ¿cómo va a leer el procesador el contenido de las direcciones 3 y 4 si solo puede acceder a los límites de cuatro bytes?
Debido a la limitación del límite de acceso a la memoria, el procesador debe acceder a la ubicación de la memoria con la dirección 0 y leer los cuatro bytes consecutivos (direcciones 0 a 3). A continuación, tiene que usar las operaciones de desplazamiento para separar el contenido de la dirección 3 de los otros tres bytes (direcciones 0 a 2). De manera similar, el procesador puede acceder a la dirección 4 y leer otro fragmento de cuatro bytes de la dirección 4 a 7. Finalmente, las operaciones de desplazamiento se pueden usar para separar el byte deseado (el rectángulo azul) de los otros tres bytes.
Si no hubiera límite de límite de acceso a la memoria, podríamos leer las direcciones 3 y 4 con un solo acceso de memoria. Sin embargo, la limitación de límites obliga al procesador a acceder a la memoria dos veces. Entonces, ¿por qué necesitamos restringir el acceso a la memoria a ciertos límites si esto dificulta la manipulación de los datos? La limitación de los límites de acceso a la memoria existe porque hacer ciertas suposiciones sobre la dirección puede simplificar el diseño del hardware. Por ejemplo, suponga que se requieren 32 bits para direccionar todos los bytes dentro de un bloque de memoria. Si limitamos la dirección a los límites de cuatro bytes, los dos bits menos significativos de la dirección de 32 bits siempre serán cero (porque la dirección siempre será divisible de manera uniforme entre cuatro). Por lo tanto, podremos utilizar 30 bits para abordar una memoria con 232 bytes
Ahora que sabemos cómo un procesador básico accede a la memoria, podemos analizar los requisitos de alineación de datos. En general, cualquier tipo de datos de K-byte C debe tener una dirección que sea múltiplo de K. Por ejemplo, un tipo de datos de cuatro bytes solo se puede almacenar en las direcciones 0, 4, 8, …; no se puede almacenar en las direcciones 1, 2, 3, 5,…. Tales restricciones simplifican el diseño del hardware de la interfaz entre el procesador y el sistema de memoria.
Como ejemplo, considere un procesador con granularidad de acceso a memoria de cuatro bytes que puede acceder a la memoria solo en los límites de cuatro bytes. Suponga que una variable de cuatro bytes se almacena en la dirección 1, como se muestra en la Figura 4 (los cuatro bytes corresponden a los cuatro colores diferentes). En este caso, necesitaremos dos accesos de memoria y un poco de trabajo adicional para leer los datos de cuatro bytes no alineados (por "no alineado"Quiero decir que se divide en dos bloques de cuatro bytes). El procedimiento se muestra en la figura.
Sin embargo, si almacenamos una variable de cuatro bytes en cualquier dirección que sea un múltiplo de 4, necesitaremos solo un acceso de memoria para modificar los datos o leerlos.
Es por eso que almacenar tipos de datos de K-byte en una dirección que es un múltiplo de K puede hacer que el sistema sea más eficiente. Por lo tanto, el lenguaje C "carbonizarseLas variables (que requieren solo un byte) pueden almacenarse en cualquier dirección de byte, pero una variable de dos bytes debe almacenarse en direcciones pares. Los tipos de cuatro bytes deben comenzar en direcciones que son divisibles por 4, y los tipos de datos de ocho bytes deben almacenarse en direcciones divisibles por 8. Por ejemplo, suponga que en una máquina en particular, "corto"Las variables requieren dos bytes,"En t”Y“flotadorLos tipos toman cuatro bytes, ylargo","doble”, Y los punteros ocupan ocho bytes. Normalmente, cada uno de estos tipos de datos debe tener una dirección que sea un múltiplo de K, donde K se muestra en la siguiente tabla.
Tipo de datos | K |
carbonizarse | 1 |
corto | 2 |
int, flotar | 4 |
largo, doble, char * | 8 |
Tenga en cuenta que el tamaño de los diferentes tipos de datos puede variar según el compilador y la arquitectura de la máquina. El operador sizeof () sería la mejor manera de encontrar el tamaño real de un tipo de datos.
Ahora, examinemos el diseño de la memoria para una estructura. Considere la posibilidad de compilar la siguiente estructura para una máquina de 32 bits:
struct Test2 {
uint8_t c;
uint32_t d;
uint8_t e;
uint16_t f;
} MyStruct;
Sabemos que se asignarán cuatro ubicaciones de memoria para almacenar los miembros dentro de la estructura, y el orden de las ubicaciones de memoria coincidirá con el de la declaración de los miembros. El primer miembro es una variable de un byte y se puede almacenar en cualquier dirección. Por lo tanto, la primera ubicación de almacenamiento disponible se asignará a esta variable. Supongamos que, como se muestra en la Figura 5, el compilador asigna la dirección 0 a esta variable. El siguiente miembro es un tipo de datos de cuatro bytes y se puede almacenar solo en direcciones que son un múltiplo de 4. La primera ubicación de almacenamiento disponible es la dirección 4. Sin embargo, esto requiere dejar las direcciones 1, 2 y 3 sin usar. Como puede ver, el requisito de alineación de datos conduce a un espacio desperdiciado (o relleno) en el diseño de la memoria.
El siguiente miembro es e, que es una variable de un byte. La primera ubicación de almacenamiento disponible (dirección 8 en la Figura 5) se puede asignar a esta variable. A continuación, llegamos a f, que es una variable de dos bytes. Puede almacenarse en una dirección que sea divisible por 2. El primer espacio disponible es la dirección 10. Como puede ver, aparecerá más relleno para satisfacer los requisitos de alineación de datos.
Esperábamos que la estructura ocupara 8 bytes, pero en realidad requiere 12 bytes. Curiosamente, si somos conscientes de los requisitos de alineación de datos, podremos reorganizar el orden de los miembros dentro de una estructura y hacer que el uso de la memoria sea más eficiente. Por ejemplo, reescribamos la estructura anterior como se indica a continuación, donde los miembros se ordenan desde el más grande hasta el más pequeño.
struct Test2 {
uint32_t d;
uint16_t f;
uint8_t c;
uint8_t e;
} MyStruct;
En una máquina de 32 bits, el diseño de memoria para la estructura anterior probablemente se verá como el diseño que se muestra en la Figura 6.
Mientras que la primera estructura requería 12 bytes, la nueva disposición solo requiere 8 bytes. Esta es una mejora significativa, especialmente en el contexto de los procesadores integrados con memoria limitada.
Además, tenga en cuenta que puede haber algunos bytes de relleno después del último miembro de una estructura. El tamaño total de una estructura debe ser divisible por el tamaño de su miembro más grande. Considera la siguiente estructura:
struct Test3 {
uint32_t c;
uint8_t d;
} MyStruct2;
En este caso, el diseño de la memoria será como se muestra en la Figura 7. Como puede ver, se agregan tres bytes de relleno al final del diseño de la memoria para aumentar el tamaño de la estructura a 8 bytes. Esto hará que el tamaño de la estructura sea divisible por el tamaño del miembro más grande dentro de la estructura (el miembro c, que es una variable de cuatro bytes).
Para ver una lista completa de mis artículos, visite esta página.
ga('create', 'UA-1454132-1', 'auto'); ga('require', 'GTM-MMWSMVL'); ga('require', 'displayfeatures'); ga('set',{'dimension1':'memory,computers-peripherals,computing,embedded,engineering-consulting'}); ga('set',{'contentGroup1':'memory,computers-peripherals,computing,embedded,engineering-consulting'});
ga('set',{'dimension3':"May 02, 2019"});
ga('set',{'dimension4':"Steve Arar"});
ga('send', 'pageview');
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js'); fbq('init', '1808435332737507'); // Insert your pixel ID here. fbq('track', 'PageView'); fbq('track', 'ViewContent', { content_ids: ['memory','computers-peripherals','computing','embedded','engineering-consulting'], content_type: 'category'});
_linkedin_data_partner_id = "353081"; (function(){var s = document.getElementsByTagName("script")[0]; var b = document.createElement("script"); b.type = "text/javascript";b.async = true; b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; s.parentNode.insertBefore(b, s);})(); } if(jstz.determine().name().indexOf("Europe") === -1) { showSocialCode(); // NOT EU } else { showSocialCode(); window.addEventListener("load", function () { window.cookieconsent.initialise({ "palette": { "popup": { "background": "#252e39" }, "button": { "background": "#14a7d0" } }, "type": "opt-out", "content": { "message": "This website uses tracking cookies to ensure you get the best experience on our website.", "href": "https://www.allaboutcircuits.com/privacy-policy/", "dismiss": "OK, GOT IT" }, onInitialise: function (status) { var type = this.options.type; var didConsent = this.hasConsented(); if (type == 'opt-out' && didConsent) { console.log("eu"); //showSocialCode(); } },
onStatusChange: function (status, chosenBefore) { var type = this.options.type; var didConsent = this.hasConsented(); if (type == 'opt-out' && didConsent) { console.log("eu"); //showSocialCode(); } },
onRevokeChoice: function () { var type = this.options.type; if (type == 'opt-out') { console.log("eu"); //showSocialCode(); } },
}) }); }
Los días felices de la PDA y Blackberry han quedado definitivamente atrás, pero el factor…
Tutorial sobre cómo pronosticar usando un modelo autorregresivo en PythonFoto de Aron Visuals en UnsplashForecasting…
Si tienes un iPhone, los AirPods Pro son la opción obvia para escuchar música, ¡aunque…
Ilustración de Alex Castro / The Verge Plus nuevos rumores sobre el quinto Galaxy Fold.…
Se rumorea que los auriculares premium de próxima generación de Apple, los AirPods Max 2,…
El desarrollador Motive Studio y el editor EA han lanzado un nuevo tráiler de la…