Ventajas y desventajas de HugePages

Ventajas y desventajas de HugePages

La traducción del artículo se ha preparado para los estudiantes del curso «Administrador de Linux».

Anteriormente hablé sobre cómo verificar y habilitar el uso de Hugepages en Linux.
Este artículo será útil solo si realmente tiene un lugar donde usar Hugepages. He encontrado a muchas personas que se engañan con la perspectiva de que Hugepages mejorará mágicamente el rendimiento. Sin embargo, el uso de hugepaging es un tema complejo y, si se usa incorrectamente, puede reducir el rendimiento.

Parte 1: verifique que hugepages estén habilitados en Linux (original aquí)

Problema:
Es necesario verificar si HugePages están habilitados en su sistema.

Solución:
Es bastante simple:

cat /sys/kernel/mm/transparent_hugepage/enabled

Recibirá algo como esto:

always [madvise] never

Verá una lista de opciones disponibles (always, madvise, never), donde la opción actualmente activa estará entre paréntesis (por defecto madvise).

madvise significa que hugepages transparentes están habilitados solo para áreas de memoria que solicitan explícitamente hugepages mediante madvise(2).

always significa que hugepages transparentes siempre están habilitados y para todos los procesos. Normalmente esto aumenta el rendimiento, pero si tiene un caso de uso donde muchos procesos consumen pequeñas cantidades de memoria, la carga total de memoria puede aumentar drásticamente.

never significa que hugepages transparentes no se habilitarán incluso si se solicitan mediante madvise. Para obtener más información, consulte el la documentación núcleo de Linux.

Cómo cambiar el valor predeterminado

Opción 1: Cambie directamente sysfs (después de reiniciar, el parámetro volverá al 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

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

  • Para establecer always como predeterminado, use:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comentar CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Para establecer madvise como predeterminado, use:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comentar CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Parte 2: Ventajas y desventajas de HugePages

Intentaremos explicar selectivamente las ventajas, desventajas y posibles errores al usar Hugepages. Dado que es un artículo tecnológicamente complejo y detallado, probablemente será difícil de entender para las personas que se engañan pensando que Hugepages son una panacea, sacrificaré la precisión en favor de la simplicidad. Simplemente vale la pena tener en cuenta que muchos temas son realmente complejos y por lo tanto se simplifican mucho.

Tenga en cuenta que estamos hablando de sistemas x86 de 64 bits que funcionan en Linux y que simplemente asumo que el sistema admite transparent hugepages (ya que no es una desventaja que las hugepages no sean sustituidas), como ocurre en prácticamente cualquier entorno moderno de Linux.

En los enlaces a continuación, adjuntaré más descripciones técnicas.

Memoria virtual

Si usted es un programador de C++, sabe que los objetos en memoria tienen direcciones específicas (valores de puntero).

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

Este enfoque tiene múltiples ventajas, pero las más básicas son:

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

¿Qué son las páginas?

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

La mayoría de las páginas con las que usted trata apuntan bien a RAM o están intercambiadas (swap), es decir, se almacenan en un disco duro o SSD. El núcleo gestiona la ubicación física de cada página. Si se accede a una página intercambiada, el núcleo detiene el hilo que intenta acceder a la memoria, lee la página desde el disco duro/SSD a la RAM y luego continúa la ejecución del hilo.

Este proceso es transparente para el hilo, es decir, no necesariamente lee directamente desde el disco duro/SSD. El tamaño de las páginas normales es de 4096 bytes. El tamaño de las Hugepages es de 2 megabytes.

Buffer de traducción asociativa (TLB)

Cuando un programa accede a alguna página de memoria, la unidad central de procesamiento debe saber desde qué página física leer los datos (es decir, debe tener un mapa de direcciones virtuales).

En el núcleo hay una estructura de datos (tabla de páginas) que contiene toda la información sobre las páginas utilizadas. Con esta estructura de datos se puede mapear una dirección virtual a una dirección física.

Sin embargo, la tabla de páginas es bastante compleja y funciona lentamente, así 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 de direcciones virtuales y físicas. Esto significa que, aunque necesitamos analizar la tabla de páginas en el primer intento de acceso, todas las solicitudes subsecuentes a la página pueden manejarse en el TLB, lo que garantiza un funcionamiento rápido.

Dado que está implementado como un dispositivo físico (lo que lo hace principalmente rápido), su capacidad es limitada. Por lo tanto, si desea acceder a más páginas, el TLB no podrá almacenar el mapeo de todas ellas, lo que hará que su programa funcione mucho más lentamente.

Las Hugepages vienen al rescate

Entonces, ¿qué podemos hacer para evitar el desbordamiento del TLB? (Suponemos que el programa aún necesita la misma cantidad de memoria).

Aquí es donde entran las Hugepages. En lugar de 4096 bytes, que requieren una única entrada en el TLB, una entrada en el TLB ahora puede apuntar a colosales 2 megabytes. Supongamos que el TLB tiene 512 entradas; aquí, sin Hugepages, podemos mapear:

4096 b⋅512=2 MB

Mientras que con ellas podemos mapear:

2 MB⋅512=1 GB

Por eso las Hugepages son geniales. Pueden aumentar el rendimiento sin un esfuerzo significativo. Pero aquí hay importantes advertencias.

Intercambio de Hugepages

El núcleo rastrea automáticamente la frecuencia de uso de cada página de memoria. Si hay una falta de memoria física (RAM), el núcleo moverá las páginas menos importantes (menos usadas) al disco duro para liberar parte de la RAM para páginas más importantes.
En principio, lo mismo ocurre con las Hugepages. Sin embargo, el núcleo solo puede intercambiar páginas completas, no bytes individuales.

Supongamos que tenemos un programa como este:

char* mymemory = malloc(2*1024*1024); // ¡Consideremos esto como una Hugepage!
// Llenemos mymemory con algunos datos
// Haremos muchas otras cosas,
// que conducirán al intercambio de la página mymemory
// ...
// Solo solicitaremos acceso al primer byte
putchar(mymemory[0]); 

En este caso, el núcleo necesitará leer 2 megabytes de información desde el disco duro/SSD solo para que ustedes lean un byte. En cuanto a las páginas normales, solo se tiene que leer 4096 bytes desde el disco duro/SSD.

Por lo tanto, si hugepage se reemplaza, su lectura es más rápida, solo si necesitas acceder a toda la página. Esto significa que si intentas acceder de manera aleatoria a diferentes partes de la memoria y solo lees un par de kilobytes, deberías usar páginas normales y no preocuparte más.

Por otro lado, si necesitas acceder a una gran parte de la memoria de manera secuencial, hugepages aumentarán tu rendimiento. Sin embargo, debes verificar esto por ti mismo (y no en un caso de software abstracto) y ver qué funciona más rápido.

Asignación en memoria

Si estás programando en C, sabes que puedes solicitar cualquier cantidad de memoria (o casi cualquier cantidad) del montón usando malloc(). Supongamos que necesitas 30 bytes de memoria:

char* mymemory = malloc(30);

Al programador le puede parecer que "solicitas" 30 bytes de memoria del sistema operativo y devuelves un puntero a alguna memoria virtual. Pero en realidad, malloc() es solo una función de C que invoca internamente a las funciones brk y sbrk para solicitar o liberar memoria del sistema operativo.

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

Aún así, para ti todo sucede sin que te des cuenta, ¿entonces por qué debería preocuparte? Porque la llamada free() no significa que la memoria se devuelva inmediatamente al sistema operativo.

Existe el concepto de fragmentación de memoria. En casos extremos, hay segmentos del montón donde solo se utilizan unos pocos bytes, mientras que todo lo que hay entre ellos ha sido liberado. (free()).

Tenga en cuenta que la fragmentación de la memoria es un tema increíblemente complejo, y hasta cambios menores en el programa pueden afectarla significativamente. En la mayoría de los casos, los programas no causan una fragmentación de memoria considerable, pero debe tener en cuenta que si hay un problema de fragmentación en alguna parte del montón, los hugepages pueden empeorar la situación.

Aplicación selectiva de hugepages

Después de leer el artículo, ha identificado qué partes de su programa pueden beneficiarse del uso de hugepages y cuáles no. Entonces, ¿debería habilitar hugepages en absoluto?

Afortunadamente, puede usar madvise(), para habilitar el hugepaging solo en aquellas áreas de la memoria donde será beneficioso.

Para comenzar, verifique que los hugepages funcionen en modo madvise(), usando la guía al inicio del artículo.

Luego, utilice madvise(), para indicar al núcleo dónde exactamente 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 una recomendación al núcleo sobre la gestión de la memoria. Esto no significa que el núcleo utilizará automáticamente hugepages para la memoria especificada.

Consulte la documentación (manpage) madvise, para aprender más sobre la gestión de la memoria y madvise(), este tema tiene una curva de aprendizaje increíblemente pronunciada. Por tanto, si realmente desea entenderlo bien, prepárese para leer y probar durante varias semanas antes de esperar algún resultado positivo.

¿Qué leer?

¿Tienes una pregunta? ¡Escríbela en los comentarios!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster