[Rendimiento] Aplicación de HugePages en la optimización general del programa

Tabla de contenido

1. Antecedentes

2. Introducción a la recuperación de música basada en huellas dactilares

3. Principio

4. El dilema de las páginas pequeñas

5. Configuración y uso de memoria de página grande

6. Efecto de optimización de la memoria de página grande

7. Escenarios de uso de memoria de página grande

8. Resumen

Uso de LD_PRELOAD


 

 

Original: https://blog.csdn.net/yutianzuijin/article/details/41912871

Hoy, les presentaré un método relativamente novedoso de optimización del rendimiento del programa, HugePages. En pocas palabras, se trata de reducir la tabla de páginas aumentando el tamaño de la página del sistema operativo, evitando así la pérdida de tablas rápidas. La información en esta área es relativamente pobre, y la mayor parte de la información en Internet introduce su aplicación en la base de datos Oracle, lo que dará a la gente la ilusión de que esta tecnología solo se puede aplicar en la base de datos Oracle. Pero, de hecho, la memoria de página enorme se puede considerar como una tecnología de optimización muy general con una amplia gama de aplicaciones. Para diferentes aplicaciones, puede traer hasta un 50% de mejora del rendimiento, y el efecto de optimización sigue siendo muy obvio. En este blog, se utilizará un ejemplo específico para introducir el uso de memoria de página grande.

       Antes de la introducción, es necesario enfatizar que la memoria de página grande también tiene un ámbito de aplicación. El programa consume muy poca memoria o la localidad de acceso a la memoria del programa es muy buena. Es difícil mejorar el rendimiento de la memoria de página grande. Por lo tanto, si el problema de optimización del programa al que se enfrenta tiene las dos características anteriores, no considere la memoria de página grande. Más adelante, explicaré en detalle por qué la memoria de página grande del programa con las dos características anteriores no es válida.

1.  Antecedentes

       Recientemente, participé en el desarrollo de proyectos de escucha y reconocimiento de música en la empresa. Para obtener más información, consulte: Recuperación de música basada en huellas dactilares , que actualmente se encuentra en la plataforma abierta de nube de voz de Sogou . Durante el proceso de desarrollo, me encontré con un problema de rendimiento muy grave. El rendimiento aún puede cumplir con los requisitos de la prueba de un solo subproceso, pero cuando se realiza la prueba de esfuerzo de varios subprocesos, la parte del algoritmo que consume más tiempo de repente se convierte en varios veces más lento! Después de una depuración cuidadosa, descubrí que el rendimiento más influyente es la opción del compilador -pg. Después de eliminarlo, el rendimiento será mucho mejor, pero seguirá siendo aproximadamente 2 veces más lento que el rendimiento de un solo subproceso, lo que provocará el real -Tiempo de tiempo del sistema para llegar a 1.0 o más., La capacidad de respuesta se reduce drásticamente.

       A través de un análisis más cuidadoso, descubrimos que la parte del sistema que consume más tiempo es el proceso de acceder a la biblioteca de huellas dactilares, pero esta parte no tiene espacio para la optimización en absoluto, y solo podemos cambiar a una máquina con un ancho de banda de memoria más alto. . Cambiar a una máquina con un mayor ancho de banda de memoria trajo muchas mejoras de rendimiento, pero aún así no cumplía con los requisitos. Justo cuando la montaña estaba agotada, el Dr. Chuntao Hong de MSRA vio accidentalmente que MSRA mencionaba en Weibo que usaban una gran memoria de página para optimizar el problema de acceso de una matriz aleatoria y obtuvieron una muy buena mejora de rendimiento. Luego le pedí ayuda. Finalmente, el rendimiento del sistema mejoró aún más mediante el método de memoria de página grande, y la tasa en tiempo real se redujo a aproximadamente 0,4. ¡Ha alcanzado la meta con éxito!

2. Introducción a la recuperación de música basada en huellas dactilares

El proceso de recuperación es en realidad el mismo que el del motor de búsqueda, la huella dactilar de la música es equivalente a las palabras clave en el motor de búsqueda y la base de datos de huellas dactilares es equivalente a la biblioteca de páginas web de fondo del motor de búsqueda. La estructura de la base de datos de huellas dactilares es la misma que la base de datos de la página web del motor de búsqueda, que adopta la forma de índice invertido. Como se muestra abajo:

 

Figura 1 Tabla de índice invertido basada en huellas dactilares

Es solo que las huellas digitales son todas un entero de tipo int (la cifra solo ocupa 24 bits), que contiene muy poca información, por lo que es necesario extraer muchas huellas digitales para completar una coincidencia, que es de unos pocos miles por segundo. Cada vez que se obtiene una huella dactilar, es necesario acceder a la biblioteca de huellas dactilares para obtener la lista invertida correspondiente, y luego construir una lista de reenvío según el id de música para analizar qué música coincide, como se muestra en la siguiente figura:

 

Figura 2 La similitud del emparejamiento estadístico

El resultado final es la música con el resultado de clasificación más alto.

La base de datos de huellas dactilares actual es de aproximadamente 60G, que es el resultado de la extracción de huellas dactilares de canciones de 25w. La longitud de la lista invertida correspondiente a cada huella dactilar no es fija, pero hay un límite superior de 7500. El número de música en la lista de la primera fila también es 25w, y el número de diferencias de tiempo más largas correspondientes a cada pieza musical es 8192. Se generarán alrededor de 1000 huellas digitales (o incluso más) en una sola búsqueda.

A través de la introducción anterior, se puede ver que la recuperación de música basada en huellas dactilares (escuchar canciones y reconocer música) tiene tres partes: 1. Extraer huellas dactilares, 2. Visitar la biblioteca de huellas dactilares, 3. Clasificar la diferencia horaria. En el caso del subproceso múltiple, las proporciones que consumen mucho tiempo de estas tres partes son aproximadamente: 1%, 80% y 19%, es decir, la mayor parte del tiempo se dedica a la operación de búsqueda de la biblioteca de huellas dactilares. Lo más problemático es que todos los accesos a la biblioteca de huellas dactilares son accesos desordenados y no hay ninguna localidad, por lo que siempre falta la caché y los métodos de optimización convencionales son ineficaces y solo se puede reemplazar con un servidor con un mayor ancho de banda de memoria.

Sin embargo, es precisamente debido a las características anteriores: un gran consumo de memoria (alrededor de 100 G), el acceso a la memoria desordenado y el acceso a la memoria es el cuello de botella, lo que hace que la memoria de página grande sea particularmente adecuada para optimizar los cuellos de botella de rendimiento encontrados anteriormente.

3. Principio

El principio de memoria de página grande implica el proceso de conversión de la dirección virtual del sistema operativo a la dirección física. Para ejecutar varios procesos al mismo tiempo, el sistema operativo proporciona un espacio de proceso virtual para cada proceso. En un sistema operativo de 32 bits, el tamaño del espacio de proceso es 4G y un sistema de 64 bits es 2 ^ 64 (en realidad , puede ser menor que este valor). Durante mucho tiempo, he estado muy confundido acerca de esto ¿Causará esto conflictos en el acceso a la memoria por parte de múltiples procesos, por ejemplo, cuando ambos procesos acceden a la dirección 0x00000010? De hecho, el espacio de proceso de cada proceso es virtual, que no es lo mismo que la dirección física. Los dos acceden a la misma dirección virtual, pero son diferentes después de la conversión a la dirección física. Esta conversión se realiza a través de la tabla de páginas, y el conocimiento involucrado es la gestión del almacenamiento de páginas del sistema operativo.

La gestión del almacenamiento de paginación divide el espacio de direcciones virtuales del proceso en varias páginas y cada página se numera. En consecuencia, el espacio de la memoria física también se divide en varios bloques, que también están numerados. El tamaño de la página y del bloque es el mismo. Suponiendo que el tamaño de cada página es 4K, la estructura de direcciones de paginación en un sistema de 32 bits es:

 

Para asegurar que el proceso pueda encontrar el bloque físico real correspondiente a la página virtual en la memoria, es necesario mantener una tabla de imágenes, es decir, la tabla de páginas, para cada proceso. La tabla de páginas registra el número de bloque físico correspondiente a cada página virtual en la memoria, como se muestra en la Figura 3. Una vez configurada la tabla de páginas, cuando se ejecuta el proceso, el número de bloque físico de cada página en la memoria se puede encontrar consultando la tabla.

Se establece un registro de tabla de páginas en el sistema operativo, que almacena la dirección de inicio de la tabla de páginas en la memoria y la longitud de la tabla de páginas. Cuando el proceso no se está ejecutando, la dirección de inicio de la tabla de páginas y la longitud de la tabla de páginas se colocan en el PCB del proceso; cuando el programador programa el proceso, estos dos datos se cargan en el registro de la tabla de páginas.

Cuando un proceso desea acceder a datos en una determinada dirección virtual, el mecanismo de conversión de dirección de paginación dividirá automáticamente la dirección efectiva (dirección relativa) en dos partes, el número de página y la dirección dentro de la página, y luego usará el número de página como un index para recuperar la tabla de páginas y buscar La operación la realiza el hardware. Si el número de página dado no excede la longitud de la tabla de páginas, agregue la dirección de inicio de la tabla de páginas al producto del número de página y la longitud de la entrada de la tabla de páginas para obtener la posición de la entrada en la tabla de páginas. y luego la página física se puede obtener de la dirección del bloque, cárguela en el registro de direcciones físicas. Al mismo tiempo, la dirección de la página en el registro de direcciones efectivas se envía al campo de direcciones de bloque del registro de direcciones físicas. Esto completa la conversión de la dirección virtual a la dirección física.

 

Figura 3 El papel de la tabla de páginas

Debido a que la tabla de páginas se almacena en la memoria, esto hace que la CPU acceda a la memoria dos veces cada vez que accede a un dato. La primera vez que se accede a la tabla de páginas en la memoria, se encuentra el número de bloque físico de la página especificada y el número de bloque se empalma con el desplazamiento en la página para formar una dirección física. Cuando se accede a la memoria por segunda vez, los datos requeridos se obtienen de la dirección obtenida la primera vez. Por lo tanto, el uso de este método reducirá la velocidad de procesamiento de la computadora en casi la mitad.

Para mejorar la velocidad de conversión de direcciones, se puede agregar una caché especial de alta velocidad con capacidad de búsqueda paralela al mecanismo de conversión de direcciones, es decir, la tabla rápida (TLB), que se utiliza para almacenar las entradas de la tabla de páginas a las que se accede actualmente. . El mecanismo de conversión de direcciones con tabla rápida se muestra en la Figura 4. Debido al costo, la tabla rápida no se puede hacer muy grande, por lo general solo se almacenan entradas de tabla de 16 ~ 512 páginas.

El mecanismo de conversión de direcciones anterior funciona muy bien para programas pequeños y medianos. La tasa de aciertos de la tabla rápida es muy alta, por lo que no traerá mucha pérdida de rendimiento, pero cuando el programa consume mucha memoria y la tasa de aciertos de la tabla rápida es no alto, entonces viene el problema.

 

Figura 4 Mecanismo de conversión de direcciones con tabla rápida

4. El dilema de las páginas pequeñas

       Todos los sistemas informáticos modernos admiten un espacio de direcciones virtuales muy grande (2 ^ 32 ~ 2 ^ 64). En tal entorno, la tabla de páginas se vuelve muy grande. Por ejemplo, asumiendo que el tamaño de la página es 4K, para un programa que ocupa 40G de memoria, el tamaño de la tabla de páginas es 10M, y también se requiere que el espacio sea contiguo. Para resolver el problema de la continuidad del espacio, puede introducir una tabla de páginas de dos o tres niveles. Pero esto afecta aún más al rendimiento, porque si falta la tabla rápida, el número de veces que se accede a la tabla de páginas cambia de dos a tres o cuatro veces. Debido a que el espacio de memoria al que puede acceder el programa es muy grande, si la localidad de acceso a la memoria del programa no es buena, siempre faltará la tabla rápida, lo que afectará seriamente el rendimiento.

       Además, debido a que las entradas de la tabla de páginas son hasta 10M, y la tabla rápida solo puede almacenar en caché unos pocos cientos de páginas, incluso si el rendimiento de acceso a la memoria del programa es muy bueno, la probabilidad de que falte la tabla rápida es muy alta en el caso de un gran consumo de memoria. Entonces, ¿hay alguna buena manera de resolver la tabla rápida que falta? ¡Gran memoria de página! Supongamos que cambiamos el tamaño de la página a 1G, y la entrada de la tabla de páginas de la memoria de 40G es solo 40, ¡y la tabla rápida no faltará en absoluto! Incluso si falta, debido a que hay pocas entradas, se puede usar una tabla de página de primer nivel, y la falta solo provocará dos recuperaciones de memoria. Esta es la razón fundamental por la que la memoria de página grande puede optimizar el rendimiento del programa: ¡casi no faltan tablas!

       Anteriormente mencionamos que si el programa a optimizar consume muy poca memoria, o si la localidad de acceso a la memoria es muy buena, el efecto de optimización de la memoria de página grande será muy insignificante, ahora debemos entender por qué. Si el programa consume muy poca memoria, como solo unos pocos megabytes, hay pocas entradas en la tabla de páginas y es probable que la tabla rápida esté completamente almacenada en caché, e incluso si falta, se puede reemplazar por la página de primer nivel. mesa. Si la localidad de acceso a la memoria del programa también es muy buena, entonces, dentro de un período de tiempo, el programa accede a la memoria adyacente, la probabilidad de perder tablas rápidas también es muy pequeña. Por lo tanto, en los dos casos anteriores, es difícil que se pierda la tabla rápida, por lo que la gran memoria de página no muestra la ventaja.

5. Configuración y uso de memoria de página grande

       Mucha información en Internet acompañará su uso en la base de datos Oracle al introducir memoria de página grande, lo que dará a la gente la ilusión de que la memoria de página grande sólo se puede utilizar en la base de datos Oracle. A través del análisis anterior, podemos saber que, de hecho, la memoria de página grande es una técnica de optimización muy general. Su método de optimización es evitar perder tablas rápidas. Entonces, cómo aplicarlo específicamente, los pasos utilizados se describen en detalle a continuación.

 

1. Instale la biblioteca libhugetlbfs

       La biblioteca libhugetlbfs implementa un gran acceso a la memoria de páginas. La instalación se puede realizar a través del comando apt-get o yum. Si el sistema no tiene este comando, también puede descargarlo del sitio web oficial .

El uso de libhugetlbfs en Linux: https://www.dazhuanlan.com/2019/11/22/5dd71081e318e/

2. Configure el archivo de inicio de grub

      Este paso es muy crítico, determina el tamaño de cada página grande que asigna y cuántas páginas grandes. La operación específica es editar el archivo /etc/grub.conf, como se muestra en la Figura 5.

 

Figura 5 secuencia de comandos de inicio grub.conf

Específicamente, agregue varios parámetros de inicio al final de la opción del kernel: transparent_hugepage = never default_hugepagesz = 1G hugepagesz = 1G hugepages = 123. De estos cuatro parámetros, los más importantes son los dos últimos. Se usa hugepagesz para establecer el tamaño de cada página. Lo configuramos en 1G. Otras configuraciones opcionales son 4K y 2M (2M es el predeterminado). Si la versión del sistema operativo es demasiado baja, es posible que falle la configuración de la página 1G, así que verifique la versión de su sistema operativo si la configuración falla. Enormes páginas se utiliza para establecer cuántas páginas de gran memoria de página, la memoria de nuestro sistema es 128G, ahora 123G está asignado para servir páginas grandes. Cabe señalar aquí que las páginas enormes asignadas son invisibles para los programas convencionales. Por ejemplo, a nuestro sistema todavía le quedan 5G de memoria ordinaria. En este momento, si inicio un programa que consume 10G según el método convencional, fallará . Después de modificar grub.conf, reinicie el sistema. Luego ejecute el comando cat / proc / meminfo | grep Huge para verificar si la configuración de la página enorme es efectiva. Si lo es, se mostrará el siguiente contenido:

 

Figura 6 Consumo actual de páginas grandes

Tenemos que centrarnos en cuatro de estos valores, HugePages_Total dijo que el número total actual de páginas grandes, HugePages_Free expresado después de que el programa esté en funcionamiento, pero también el número restante de páginas grandes, HugePages_Rsvd representa el número de retención total de HugePages del sistema actual, y más específicamente, el punto se refiere al programa. El sistema se aplica, pero debido a que el programa no tiene operaciones sustanciales de lectura y escritura de HugePages, el sistema en realidad no ha asignado el número de HugePages al programa. Hugepagesize representa el tamaño de cada página enorme, que es de 1 GB aquí.

       Encontramos un problema en nuestros experimentos: el valor de Free y el valor de Rsvd pueden ser diferentes del significado literal. Si la página grande que solicitamos no es suficiente para iniciar el programa desde el principio, el sistema mostrará el siguiente error:

ibhugetlbfs: ADVERTENCIA: Nuevo mapa de segmento de montón en 0x40000000 falló: no se puede asignar memoria

En este punto, al mirar de nuevo los cuatro valores anteriores se encontrará una situación de este tipo: HugePages_Free es igual a a, y HugePages_Rsvd es igual a a. Esto hace que la gente se sienta muy extraña, obviamente todavía quedan páginas grandes, pero el sistema informa de un error que indica que la asignación de páginas grandes ha fallado. Después de muchos intentos, creemos que las páginas grandes en Rsvd deberían incluirse en Free, por lo que cuando Free es igual a Rsvd, en realidad no hay páginas grandes disponibles. Free minus Rsvd es la página enorme que se puede volver a asignar. Por ejemplo, en la Figura 6 hay 16 páginas grandes que se pueden asignar.

¿Cuántas páginas grandes deben asignarse es apropiado? Esto requiere varios intentos. Una experiencia que hemos aprendido es que el uso de páginas grandes por subprocesos es muy derrochador. Es mejor asignar todo el espacio en el subproceso principal y luego asignar a cada subproceso, esto reducirá significativamente el desperdicio de páginas grandes.

 

3. montar

Ejecute mount para asignar la memoria de página grande a un directorio vacío. Puede ejecutar los siguientes comandos:

 

if [ ! -d /search/music/libhugetlbfs ]; then
    mkdir /search/music/libhugetlbfs
fi
mount -t hugetlbfs hugetlbfs /search/music/libhugetlbfs

 

4. Ejecute la aplicación

Para habilitar páginas grandes, no puede iniciar la aplicación de la forma habitual, debe iniciarla en el siguiente formato:

HUGETLB_MORECORE = sí LD_PRELOAD = libhugetlbfs .so ./your_program

Este método cargará la biblioteca libhugetlbfs para reemplazar la biblioteca estándar. La operación específica es reemplazar el malloc estándar con un malloc de página grande. En este punto, la memoria solicitada por el programa es una gran memoria de página.

Siga los cuatro pasos anteriores para habilitar la memoria de página grande, por lo que es fácil habilitar páginas grandes.

 

 

6. Efecto de optimización de la memoria de página grande

Si su aplicación está fuera de servicio, el acceso a la memoria es muy grave, entonces la memoria de página grande traerá beneficios relativamente grandes, sucede que ahora estamos escuchando canciones y reconociendo la música es una aplicación, por lo que el efecto de optimización es obvio, el la siguiente es la biblioteca de música es 25w Cuando, la ejecución del programa con y sin páginas grandes está habilitada.

Se puede ver que después de que se habilita la memoria de página grande, el tiempo de acceso del programa se reduce significativamente y el rendimiento mejora en casi un 50%, lo que cumple con los requisitos de rendimiento.

7. Escenarios de uso de memoria de página grande

Cualquier método de optimización tiene su ámbito de aplicación y la gran memoria de página no es una excepción. Siempre hemos enfatizado que solo una gran memoria de página que consume una gran cantidad de memoria, los accesos aleatorios y el acceso a la memoria es el cuello de botella del programa que traerá una mejora significativa del rendimiento. En nuestro sistema de escucha y reconocimiento de música, el consumo de memoria se acerca a los 100G, y los accesos a la memoria son todos accesos desordenados, por lo que aporta una mejora significativa en el rendimiento. No es descabellado que los ejemplos en línea hayan utilizado la base de datos Oracle como ejemplo, ya que la memoria consumida por la base de datos Oracle también es enorme y la adición, eliminación y modificación de la base de datos carece de localidad. Las adiciones, eliminaciones y modificaciones detrás de la base de datos son básicamente operaciones en árboles B, y las operaciones de árbol generalmente carecen de localidad.

¿Qué tipo de programa tiene localidad pobre? Personalmente, creo que los programas implementados utilizando estrategias hash y de árbol a menudo tienen una localidad de acceso a memoria deficiente. En este momento, si el rendimiento del programa no es bueno, puede probar con una memoria de página grande. Por el contrario, operaciones como el recorrido de matriz simple o el recorrido de ancho de gráfico tienen una buena localidad de acceso a la memoria y es difícil lograr una mejora del rendimiento utilizando una gran memoria de página. He intentado habilitar la memoria de página grande en el decodificador de reconocimiento de voz Sogou, con la esperanza de mejorar el rendimiento, pero el efecto es decepcionante y el rendimiento no mejora. Esto se debe a que el decodificador de reconocimiento de voz es esencialmente una búsqueda amplia de imágenes, con una buena ubicación de acceso a la memoria, y el acceso a la memoria no es un cuello de botella en el rendimiento. En este momento, el uso de memoria de página grande puede traer otros gastos generales, lo que da como resultado una degradación del rendimiento.

8. Resumen

Este blog presenta el principio y el uso de la memoria de página grande en detalle con el ejemplo de escuchar música y reconocer música. Debido a la proliferación de big data, la cantidad de datos procesados ​​por las aplicaciones actuales está aumentando y el acceso a los datos es cada vez más irregular, estas condiciones hacen posible el uso de memoria de gran tamaño. Entonces, si su programa se ejecuta lentamente y satisface las condiciones de uso de la memoria de página grande, entonces pruébelo. De todos modos, es muy simple y no tiene pérdidas, en caso de que pueda traer buenos resultados.

 

Uso de LD_PRELOAD

(Inglés: https://catonmat.net/simple-ld-preload-tutorial )

LD_PRELOAD es una variable de entorno utilizada para cargar bibliotecas dinámicas. La prioridad de carga de la biblioteca dinámica es la más alta. En general, el orden de carga es LD_PRELOAD> LD_LIBRARY_PATH> /etc/ld.so.cache> / lib> / usr / lib. En el programa, a menudo necesitamos llamar a algunas funciones de biblioteca externas. Tome malloc como ejemplo. Si tenemos una función malloc personalizada, compílela en una biblioteca dinámica y cárguela a través de LD_PRELOAD. Cuando se llama a la función malloc en el programa, la llamada es en realidad Es nuestra función personalizada, tomemos un ejemplo para ilustrarlo.

// test.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i = 0;
    for (; i < 5; ++i) {
        char *c = (char*)malloc(sizeof(char));
        if (NULL == c) {
            printf("malloc fails\n");
        }
        else {
            printf("malloc ok\n");
        }
    }

    return 0;
}

Compile y ejecute, y los resultados son los siguientes:

$gcc -o test test.c
$./test
malloc ok
malloc ok
malloc ok
malloc ok
malloc ok

Se puede observar que no hay ningún problema con el programa en ejecución, hacemos una pequeña modificación y personalizamos malloc.

// preload.c
#include <stdio.h>
#include <stdlib.h>

void* malloc(size_t size)
{
    printf("%s size: %lu\n", __func__, size);
    return NULL;
}

Luego empaquete el malloc personalizado como una biblioteca dinámica.

$gcc -shared -fpic -o libpreload.so preload.c

Luego use LD_PRELOAD para cargar libpreload.so y vea qué sucede:

$LD_PRELOAD=./libpreload.so ./test
malloc size: 1
malloc fails
malloc size: 1
malloc fails
malloc size: 1
malloc fails
malloc size: 1
malloc fails
malloc size: 1
malloc fails

Como puede ver, malloc devuelve NULL 5 veces (es decir, llamamos al malloc definido por nosotros mismos). Si no sabe que LD_PRELOAD está haciendo el truco, es posible que no pueda encontrar la razón por un largo tiempo después del análisis. Este LD_PRELOAD es un arma de doble filo. Si lo usa bien, puede ayudarnos. Si tiene motivos ocultos, puede tener sorpresas inesperadas.

" Tenga en cuenta la variable de entorno LD_PRELOAD en UNIX " https://blog.csdn.net/haoel/article/details/1602108

Supongo que te gusta

Origin blog.csdn.net/bandaoyu/article/details/113559126
Recomendado
Clasificación