c++ multithreading performance

c++ - ¿Por qué mi biblioteca de registro hace que las pruebas de rendimiento se ejecuten más rápido?



multithreading performance (1)

Cuando se accede por primera vez a la memoria no inicializada, las fallas de página afectarán el tiempo.

Entonces, antes de su primera llamada a, std::chrono::steady_clock::now() , inicie la memoria ejecutando memset () en su sample_buffer.

Pasé el último año desarrollando una biblioteca de registro en C ++ con el rendimiento en mente. Para evaluar el rendimiento, desarrollé un conjunto de puntos de referencia para comparar mi código con otras bibliotecas, incluido un caso base que no realiza ningún registro.

En mi último punto de referencia, mido el tiempo total de ejecución de una tarea intensiva de CPU mientras el registro está activo y cuando no lo está. Luego puedo comparar el tiempo para determinar la cantidad de sobrecarga que tiene mi biblioteca. Este gráfico de barras muestra la diferencia en comparación con mi caso base no registrado.

Como puede ver, mi biblioteca ("imprudente") agrega una sobrecarga negativa (a menos que los 4 núcleos de la CPU estén ocupados). El programa se ejecuta aproximadamente medio segundo más rápido cuando el registro está habilitado que cuando está deshabilitado.

Sé que debería intentar aislar esto en un caso más simple en lugar de preguntar sobre un programa de 4000 líneas. Pero hay tantos lugares para lo que eliminar, y sin una hipótesis, simplemente haré desaparecer el problema cuando trate de aislarlo. Probablemente podría pasar otro año haciendo esto. Espero que la experiencia colectiva de Stack Overflow haga de este un problema mucho más superficial o que la causa sea obvia para alguien que tenga más experiencia que yo.

Algunos datos sobre mi biblioteca y los puntos de referencia:

  • La biblioteca consta de una API de usuario que envía los argumentos de registro a una cola sin bloqueo (Boost.Lockless) y un hilo de fondo que realiza el formato de cadenas y escribe las entradas de registro en el disco.
  • El tiempo se basa en simplemente llamar a std::chrono::steady_clock::now() al comienzo y al final del programa, e imprimir la diferencia.
  • El punto de referencia se ejecuta en una CPU Intel de 4 núcleos (i7-3770K).
  • El programa de referencia calcula un fractal de 1024x1024 de Mandelbrot y registra estadísticas sobre cada píxel, es decir, escribe alrededor de un millón de entradas de registro.
  • El tiempo de ejecución total es de aproximadamente 35 segundos para el caso de hilo de trabajo único. Entonces, el aumento de velocidad es de aproximadamente 1.5%.
  • El punto de referencia produce un archivo de salida (esto no es parte del código temporizado) que contiene el fractal generado de Mandelbrot. He verificado que se produce la misma salida cuando el registro está activado y desactivado.
  • El punto de referencia se ejecuta 100 veces (con todas las bibliotecas de referencia, esto toma alrededor de 10 horas). El gráfico de barras muestra el tiempo promedio y las barras de error muestran el rango intercuartílico.
  • Código fuente para el cálculo de Mandelbrot
  • Código fuente para el punto de referencia .
  • La raíz del repositorio de código y la documentación .

Mi pregunta es, ¿cómo puedo explicar el aparente aumento de velocidad cuando mi biblioteca de registro está habilitada?

Editar : Esto se resolvió después de probar las sugerencias dadas en los comentarios. Mi objeto de registro se crea en la línea 24 de la prueba de referencia . Aparentemente, cuando LOG_INIT () toca el objeto de registro, desencadena un error de página que causa que algunas o todas las páginas del almacenamiento intermedio de imágenes se asignen a la memoria física. Todavía no estoy seguro de por qué esto mejora el rendimiento en casi medio segundo; incluso sin el objeto de registro, lo primero que ocurre en la función mandelbrot_thread () es una escritura en la parte inferior del buffer de imagen, que debería tener un efecto similar. Pero, en cualquier caso, borrar el búfer con un memset () antes de iniciar el benchmark hace que todo sea más sensato. Los puntos de referencia actuales están here

Otras cosas que probé son:

  • Ejecútelo con el perfilador oprofile. Nunca pude lograr que se registrara en cualquier momento en las cerraduras, incluso después de ampliar el trabajo para que funcione durante aproximadamente 10 minutos. Casi todo el tiempo estuvo en el ciclo interno de la computación de Mandelbrot. Pero tal vez podría interpretarlos de manera diferente ahora que sé sobre las fallas de la página. No pensé en verificar si la escritura de la imagen tomaba una cantidad desproporcionada de tiempo.
  • Quitando las cerraduras. Esto tuvo un efecto significativo en el rendimiento, pero los resultados aún eran extraños y, de todos modos, no pude hacer el cambio en ninguna de las variantes multiproceso.
  • Compare el código ensamblador generado. Hubo diferencias, pero la construcción de registro claramente estaba haciendo más cosas. Nada se destacó como un asesino de rendimiento obvio.