Ventajas y desventajas de HugePages

Ventajas y desventajas de HugePages

Traducción del artículo elaborado para los alumnos del curso. "Administrador de Linux".

Anteriormente, hablé sobre cómo probar y habilitar Hugepages en Linux.
Este artículo sólo será útil si realmente tienes un lugar donde usar Hugepages. He conocido a mucha gente que se deja engañar por la perspectiva de que Hugepages mejorará mágicamente la productividad. Sin embargo, la paginación enorme es un tema complejo y puede degradar el rendimiento si se usa incorrectamente.

Parte 1: Verificar que las páginas enormes estén habilitadas en Linux (original aquí)

Problema:
Debe verificar si HugePages está habilitado en su sistema.

solución:
Es bastante simple:

cat /sys/kernel/mm/transparent_hugepage/enabled

Obtendrá algo como esto:

always [madvise] never

Verá una lista de opciones disponibles (siempre, madvise, nunca), y la opción actualmente activa estará entre paréntesis (por defecto loco).

loco significa que transparent hugepages habilitado solo para áreas de memoria que solicitan explícitamente páginas enormes usando madvise (2).

hacerlo significa que transparent hugepages siempre habilitado para todos los procesos. Por lo general, esto mejora el rendimiento, pero si tiene un caso de uso en el que muchos procesos consumen una pequeña cantidad de memoria, la carga general de memoria puede aumentar drásticamente.

nunca significa que transparent hugepages no se incluirán incluso cuando se soliciten mediante madvise. Para obtener más información, póngase en contacto documentación Núcleos de Linux.

Cómo cambiar el valor predeterminado

1 opción: Cambiar directamente sysfs (después de reiniciar el parámetro volverá a su valor predeterminado):

echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled

2 opción: Cambie el valor predeterminado del sistema recompilando el kernel con una configuración modificada (esta opción solo se recomienda si está utilizando un kernel personalizado):

  • Para configurar siempre de forma predeterminada, use:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Para configurar madvise como predeterminado, use:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Parte 2: Ventajas y desventajas de HugePages

Intentaremos explicar de forma selectiva las ventajas, desventajas y posibles inconvenientes del uso de Hugepages. Dado que un artículo pedante y tecnológicamente complejo probablemente será difícil de entender para las personas que se engañan al pensar que Hugepages es una panacea, sacrificaré la precisión por la simplicidad. Sólo vale la pena tener en cuenta que muchos de los temas son realmente complejos y, por lo tanto, están muy simplificados.

Tenga en cuenta que estamos hablando de sistemas x64 de 86 bits que ejecutan Linux, y que simplemente estoy asumiendo que el sistema admite páginas enormes transparentes (ya que no es una desventaja que las páginas enormes no se sobrescriban), como es el caso en casi cualquier Linux moderno. ambiente.

Adjuntaré una descripción más técnica en los enlaces a continuación.

Memoria virtual

Si eres programador de C++, sabrás que los objetos en la memoria tienen direcciones específicas (valores de puntero).

Sin embargo, estas direcciones no reflejan necesariamente las direcciones físicas en la memoria (direcciones RAM). Representan direcciones en la memoria virtual. El procesador tiene un módulo MMU (unidad de administración de memoria) especial que ayuda al núcleo a asignar la memoria virtual a una ubicación física.

Este enfoque tiene muchas ventajas, pero las más importantes son:

  • Rendimiento (por diversas razones);
  • Aislamiento del programa, es decir, ningún programa puede leer de la memoria de otro programa.

¿Qué son las páginas?

La memoria virtual se divide en páginas. Cada página individual apunta a una memoria física específica, puede apuntar a un área de la RAM o puede apuntar a una dirección asignada a un dispositivo físico, como una tarjeta de video.

La mayoría de las páginas con las que trabaja apuntan a la RAM o se intercambian, lo que significa que se almacenan en su disco duro o SSD. El kernel gestiona el diseño físico de cada página. Si se accede a una página falsificada, el kernel detiene el hilo que intenta acceder a la memoria, lee la página del disco duro/SSD en la RAM y luego continúa ejecutando el hilo.

Este proceso es transparente, lo que significa que no necesariamente lee directamente desde el HDD/SSD. El tamaño de las páginas normales es de 4096 bytes. El tamaño de Hugepages es de 2 megabytes.

Búfer asociativo de traducción (TLB)

Cuando un programa accede a una página de memoria, la CPU debe saber de qué página física leer datos (es decir, tener un mapa de direcciones virtuales).

El kernel tiene una estructura de datos (tabla de páginas) que contiene toda la información sobre las páginas que se utilizan. Con esta estructura de datos, puede asignar una dirección virtual a una dirección física.

Sin embargo, la tabla de páginas es bastante compleja y lenta, por lo que simplemente no podemos analizar toda la estructura de datos cada vez que un proceso accede a la memoria.

Afortunadamente, nuestro procesador tiene un TLB que almacena en caché el mapeo entre direcciones virtuales y físicas. Esto significa que, aunque necesitamos analizar la tabla de páginas en el primer intento de acceso, todos los accesos posteriores a la página se pueden manejar en el TLB, lo que permite una operación rápida.

Debido a que se implementa como un dispositivo físico (lo que lo hace rápido en primer lugar), su capacidad es limitada. Entonces, si desea acceder a más páginas, el TLB no podrá almacenar asignaciones para todas ellas, lo que hará que su programa se ejecute mucho más lento.

Hugepages viene al rescate

Entonces, ¿qué podemos hacer para evitar el desbordamiento de TLB? (Asumimos que el programa todavía necesita la misma cantidad de memoria).

Aquí es donde entra en juego Hugepages. En lugar de 4096 bytes que requieren solo una entrada TLB, una entrada TLB ahora puede apuntar a la friolera de 2 megabytes. Supongamos que el TLB tiene 512 entradas, aquí sin Hugepages podemos igualar:

4096 b⋅512=2 MB

Entonces, ¿cómo podemos comparar con ellos?

2 MB⋅512=1 GB

Por eso Hugepages es increíble. Pueden mejorar la productividad sin mucho esfuerzo. Pero aquí hay importantes advertencias.

Suplantación de páginas enormes

El kernel monitorea automáticamente la frecuencia con la que se usa cada página de memoria. Si no hay suficiente memoria física (RAM), el kernel moverá páginas menos importantes (usadas con menos frecuencia) al disco duro para liberar algo de RAM para páginas más importantes.
En principio, lo mismo se aplica a Hugepages. Sin embargo, el kernel sólo puede intercambiar páginas enteras, no bytes individuales.

Digamos que tenemos un programa como este:

char* mymemory = malloc(2*1024*1024); // Возьмем это за одну Hugepage!
// Заполним mymemory какими-либо данными
// Сделаем много других вещей,
// которые приведут к подмене страницы mymemory
// ...
// Запросим доступ только к первому байту
putchar(mymemory[0]); 

En este caso, el kernel necesitará reemplazar (leer) hasta 2 megabytes de información del disco duro/SSD solo para que usted pueda leer un byte. En cuanto a las páginas normales, sólo es necesario leer 4096 bytes del disco duro/SSD.

Por lo tanto, si se anula la página enorme, la lectura solo será más rápida si necesita acceder a la página completa. Esto significa que si estás intentando acceder aleatoriamente a diferentes partes de la memoria y solo estás leyendo un par de kilobytes, debes usar páginas normales y no preocuparte por nada más.

Por otro lado, si necesita acceder a una gran parte de la memoria de forma secuencial, Hugepages mejorará su rendimiento. Sin embargo, debe probarlo usted mismo (no con software abstracto) y ver qué funciona más rápido.

Asignación en memoria

Si escribe C, sabrá que puede solicitar cantidades arbitrariamente pequeñas (o casi arbitrariamente grandes) de memoria del montón usando malloc(). Digamos que necesitas 30 bytes de memoria:

char* mymemory = malloc(30);

A un programador le puede parecer que está “solicitando” 30 bytes de memoria del sistema operativo y devolviendo un puntero a alguna memoria virtual. Pero en realidad malloc () es solo una función C que llama desde dentro de la función brk y sbrk para solicitar o liberar memoria del sistema operativo.

Sin embargo, solicitar cada vez más memoria para cada asignación es ineficiente; lo más probable es que ya se haya liberado algún segmento de memoria (free()), y podemos reutilizarlo. malloc() implementa algoritmos bastante complejos para reutilizar la memoria liberada.

Al mismo tiempo, todo pasa desapercibido para ti, así que ¿por qué debería preocuparte? Pero debido a que el desafío free() no significa eso la memoria necesariamente se devuelve inmediatamente al sistema operativo.

Existe algo llamado fragmentación de la memoria. En casos extremos, hay segmentos de montón en los que solo se utilizan unos pocos bytes, mientras que todo lo que hay en el medio se ha liberado. (free()).

Tenga en cuenta que la fragmentación de la memoria es un tema increíblemente complejo e incluso cambios menores en un programa pueden tener un impacto significativo. En la mayoría de los casos, los programas no causarán una fragmentación significativa de la memoria, pero debe tener en cuenta que si hay un problema de fragmentación en alguna área del montón, las páginas enormes pueden empeorar la situación.

Uso selectivo de páginas enormes.

Después de leer este artículo, habrá determinado qué partes de su programa pueden beneficiarse del uso de páginas enormes y cuáles no. Entonces, ¿deberían habilitarse enormes páginas?

Por suerte puedes usar madvise()para habilitar la paginación enorme solo para aquellas áreas de memoria donde sería útil.

Primero, verifique que Hugepages se esté ejecutando en modo madvise() usando instrucciones al principio del artículo.

Entonces, usa madvise()para indicarle al kernel exactamente dónde usar Hugepages.

#include <sys/mman.h>
// Аллоцируйте большое количество памяти, которую будете использовать
size_t size = 256*1024*1024;
char* mymemory = malloc(size);
// Просто включите hugepages…
madvise(mymemory, size, MADV_HUGEPAGE);
// … и задайте следующее
madvise(mymemory, size, MADV_HUGEPAGE | MADV_SEQUENTIAL)

Tenga en cuenta que este método es simplemente un consejo para el núcleo sobre cómo administrar la memoria. Esto no significa que el kernel utilizará automáticamente páginas enormes para una memoria determinada.

Consulte la documentación (página de manual) aconsejarpara obtener más información sobre la gestión de la memoria y madvise(), este tema tiene una curva de aprendizaje increíblemente pronunciada. Entonces, si tiene la intención de ser realmente bueno en esto, prepárese para leer y realizar pruebas durante algunas semanas antes de esperar resultados positivos.

¿Qué leer?

¿Tengo una pregunta? ¡Escribe en los comentarios!

Fuente: habr.com

Añadir un comentario