Categoría de memoria
La memoria moderna incluye los siguientes tipos:
Registro | La memoria que interactúa directamente con la cpu para los datos (el más rápido) interactúa con la caché de nivel 1 de cpu |
caché de nivel 1 de cpu | Incrustado directamente dentro de la cpu, único en cada cpu |
caché de nivel de cpu 2 | Fuera de la cpu, también es único para cada cpu |
caché de cpu 3 | Caché compartida por todas las CPU |
Ram | Interactúe con la caché, controle dma y memoria a través de la cpu para interactuar |
disco duro | Interacción entre dma y memoria a través del control de cpu |
La velocidad de memoria en la figura anterior disminuye secuencialmente de arriba a abajo.
Utilizamos el comando sysctl -a para comprobar el tamaño de la primera, segunda y tercera caché de nivel
El tamaño de la caché de primer nivel es 32k, el segundo nivel es 256k, el tercer nivel es 6m
Caché
La estructura interna de la memoria caché:
Dado que la velocidad de la CPU es demasiado rápida, la memoria no puede mantenerse al día con la velocidad de cálculo de la cpu en absoluto, por lo que introdujimos una memoria caché avanzada cpu. Una cpu de 20 ghz puede tener 2 mil millones de ciclos de reloj por segundo, lo que significa que funciona con 2 mil millones de instrucciones. La transmisión de memoria por sí sola no puede mantenerse al día con la velocidad en absoluto
Desde la figura anterior, podemos ver que la velocidad de acceso a la caché de nivel 1 y la velocidad de acceso a la memoria son 100 veces diferentes.
Entonces, ¿por qué no reemplazar la memoria con la memoria caché?
- En primer lugar, la razón por la que el almacenamiento en caché es rápido. Una de las razones es porque está cerca de la cpu, limitaciones físicas
- El costo del almacenamiento en caché es mucho más caro que el costo de la memoria.
Combinando las dos razones anteriores, sólo podemos limitar el tamaño de la memoria caché.
Principio de localidad
Con el fin de hacer la memoria caché más eficaz, se introduce el principio de la localidad del tiempo y la localidad espacial.
El principio de la localidad temporal
Incluso si el algoritmo lru se utiliza para garantizar que los datos en caliente siempre están en la memoria caché, no necesitamos visitar la memoria con frecuencia
LruSíMenos utilizado recientementeLa abreviatura de, es decir, la menos utilizada recientemente, es un algoritmo de reemplazo de página comúnmente utilizado, seleccionando la página menos utilizada recientemente para ser eliminada
Principio de localidad espacial
Suponiendo que hayamos accedido y cargado los datos actuales, la siguiente pieza de datos se cargará definitivamente. Con la localidad espacial, la siguiente pieza de datos se cargará en la memoria caché.
Echemos un vistazo a un fragmento de código y analicemos su rendimiento:
public static void main(String[] args) {
int[] arr = new int[64 * 1024 * 1024];//64kb
long start = System.currentTimeMillis();
// loop 1
for (int i = 0; i < arr.length; i++) arr[i] *= 3;
long end = System.currentTimeMillis();
System.out.println("Time spent is " + (end - start) + "ms");
start = System.currentTimeMillis();
// loop 2
for (int i = 0; i < arr.length; i += 16) arr[i] *= 3;
end = System.currentTimeMillis();
System.out.println("Time spent is " + (end - start) + "ms");
}
El resultado de la salida es
El tiempo empleado es de 31 ms
El tiempo empleado es de 26 ms
Primero analicemos el bucle 1, que ejecutó 64 * 1024 * 1024 veces arr[i] *= 3
El segundo bucle, cada vez +16, significa 4 * 1024 * 1024 veces ejecutado arr[i] *= 3
El número de ejecuciones de arr[i] *= 3 entre los dos es 16 veces diferente, pero el tiempo de ejecución está a sólo 5 ms de distancia, hay un problema!
Echemos un vistazo a la siguiente imagen
Para asignar nuestra memoria caché razonablemente, dividimos la memoria caché en bloques, donde cada línea de caché de bloques es de 64 bytes (un byte es igual a 8 bits).
Entonces, ¿cómo corresponde nuestra caché a la memoria? La magnitud de la memoria caché y la magnitud de la memoria no son las mismas.
Usamos el método que se muestra en la figura anterior. Nuestro bloque de caché tiene un total de 8 bloques, y los últimos 3 bits de la dirección en la memoria se toman como la dirección del bloque de caché. Un método similar, 5%8=0, 21%8=5 y, a continuación, en este momento, la memoria caché alcanza el bloque de caché 5.
Por lo tanto, si los datos de memoria que consultamos están en el bloque de caché, no es necesario buscar la memoria.
Pero tanto block21 como block5 en la memoria colocarán los datos en la línea de caché5. ¿Qué tal esta solución?
Colocamos un bit de indicador en cada línea de caché para indicar a qué grupo pertenece actualmente, block5 o block21. De hecho, tomamos los dos bits altos de la dirección de memoria para determinar a qué bloque de memoria pertenece la línea de caché
Así que vamos a ordenar los pasos para obtener datos de la memoria caché
- Según la dirección de memoria, tome los últimos tres bits para localizar la línea de caché.
- También hay información en la línea de caché para identificar si la información actual es válida, si los datos no se leen de la memoria al principio
- Determine si la línea de caché es el grupo que se va a buscar, es decir, compare los dos bits altos de la memoria con la información de grupo en el línea de caché.
- Según el bit offset de la dirección de memoria, lea los datos de los datos del bloque de datos
La figura anterior es la relación de asignación entre memoria y caché, y la cpu puede obtener los datos reales directamente de la línea de caché.
A continuación, podemos explicar por qué el bucle de código 1 anterior se ejecuta para 31ms, el loop 2 se ejecuta para 26ms.
Suponiendo que solo se utilice la caché de primer nivel: 1kb puede almacenar 16 líneas de caché (1024/64), y la caché de primer nivel tiene 16 * 32 = 512 líneas de caché
Dado que cada tipo int ocupa 4 bytes, cuando se ejecuta 16 veces, ya ha ejecutado 64 bytes, que es una línea de caché, y cada 64 bytes se toma de la memoria a la línea de caché, por lo que la velocidad es aproximadamente la misma.
.