Estamos buscando actualizar el sistema operativo en nuestros servidores de Ubuntu 10.04 LTS a Ubuntu 12.04 LTS. Desafortunadamente, parece que la latencia para ejecutar un hilo que se ha vuelto ejecutable ha aumentado significativamente del kernel 2.6 al kernel 3.2. De hecho, los números de latencia que estamos obteniendo son difíciles de creer.
Déjame ser más específico sobre la prueba. Tenemos un programa que ejecuta dos subprocesos. El primer hilo obtiene la hora actual (en tics usando RDTSC) y luego señala una variable de condición una vez por segundo. El segundo subproceso espera la variable de condición y se despierta cuando se le indica. Luego obtiene la hora actual (en tics usando RDTSC). La diferencia entre el tiempo del segundo hilo y el tiempo del primer hilo se calcula y se muestra en la consola. Después de esto, el segundo subproceso espera una vez más la variable de condición. El primer hilo volverá a señalarlo después de aproximadamente un segundo paso.
Entonces, en pocas palabras, obtenemos una comunicación de hilo a hilo a través de la medición de latencia de la variable de condición una vez por segundo como resultado.
En el kernel 2.6.32, esta latencia está en algún lugar del orden de 2.8-3.5 us, lo cual es razonable. En el kernel 3.2.0, esta latencia se ha incrementado en algún lugar del orden de 40-100 us. He excluido cualquier diferencia de hardware entre los dos hosts. Se ejecutan en hardware idéntico (procesadores X5687 {Westmere-EP} de doble zócalo que funcionan a 3.6 GHz con hyperthreading, speedtep y todos los estados C desactivados). La aplicación de prueba cambia la afinidad de los subprocesos para ejecutarlos en núcleos físicos independientes del mismo socket (es decir, el primer subproceso se ejecuta en Core 0 y el segundo subproceso se ejecuta en Core 1), por lo que no hay rebotes de subprocesos en núcleos o rebote / comunicación entre sockets.
La única diferencia entre los dos hosts es que uno ejecuta Ubuntu 10.04 LTS con kernel 2.6.32-28 (el cuadro de cambio de contexto rápido) y el otro ejecuta el último Ubuntu 12.04 LTS con kernel 3.2.0-23 (el contexto lento caja de interruptores). Todas las configuraciones y el hardware del BIOS son idénticos.
¿Ha habido algún cambio en el kernel que pueda explicar esta ridícula ralentización en el tiempo que tarda en programarse la ejecución de un hilo?
Actualización: si desea ejecutar la prueba en su host y compilación de Linux, he publicado el código en pastebin para su lectura. Compilar con:
g++ -O3 -o test_latency test_latency.cpp -lpthread
Ejecute con (suponiendo que tenga al menos una caja de doble núcleo):
./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1
Actualización 2 : Después de buscar mucho en los parámetros del kernel, publicaciones sobre cambios en el kernel e investigación personal, he descubierto cuál es el problema y he publicado la solución como respuesta a esta pregunta.
fuente
/proc/sys/kernel/*
puede funcionar. Si encuentra algo que funcione, coloque esa configuración/etc/sysctl.conf
o un archivo/etc/sysctl.d/
para que persista entre reinicios.Respuestas:
La solución al problema de rendimiento de
intel_idle
activación de subprocesos incorrectos en los núcleos recientes tiene que ver con el cambio al controlador cpuidle deacpi_idle
, el controlador utilizado en los núcleos más antiguos. Lamentablemente, elintel_idle
controlador ignora la configuración de BIOS del usuario para los estados C y baila a su propio ritmo . En otras palabras, incluso si deshabilita por completo todos los estados C en el BIOS de su PC (o servidor), este controlador los activará durante períodos de inactividad breve, que casi siempre ocurren a menos que un punto de referencia sintético que consuma todos los núcleos (por ejemplo, estrés ) Esta corriendo. Puede monitorear las transiciones de estado C, junto con otra información útil relacionada con las frecuencias del procesador, utilizando la maravillosa herramienta Google i7z en la mayoría de hardware compatible.Para ver qué controlador de cpuidle está actualmente activo en su configuración, simplemente coloque el
current_driver
archivo en lacpuidle
sección de la/sys/devices/system/cpu
siguiente manera:Si desea que su sistema operativo Linux moderno tenga la latencia de cambio de contexto más baja posible, agregue los siguientes parámetros de arranque del kernel para deshabilitar todas estas funciones de ahorro de energía:
En Ubuntu 12.04, puede hacer esto agregándolos a la
GRUB_CMDLINE_LINUX_DEFAULT
entrada/etc/default/grub
y luego ejecutándolosupdate-grub
. Los parámetros de arranque para agregar son:Aquí están los detalles sangrientos sobre lo que hacen las tres opciones de arranque:
Establecer
intel_idle.max_cstate
en cero revertirá su controlador cpuidle aacpi_idle
(al menos según la documentación de la opción) o lo deshabilitará por completo. En mi caja está completamente deshabilitado (es decir, mostrar elcurrent_driver
archivo/sys/devices/system/cpu/cpuidle
produce una salida denone
). En este caso, la segunda opción de arranque noprocessor.max_cstate=0
es necesaria. Sin embargo, la documentación indica que establecer max_cstate en cero para elintel_idle
controlador debería revertir el sistema operativo alacpi_idle
controlador. Por lo tanto, puse la segunda opción de arranque por si acaso.La
processor.max_cstate
opción establece el estado C máximo para elacpi_idle
controlador en cero, con suerte también lo desactiva. No tengo un sistema en el que pueda probar esto, porqueintel_idle.max_cstate=0
elimina por completo el controlador de cpuidle en todo el hardware disponible para mí. Sin embargo, si su instalación lo revierte deintel_idle
aacpi_idle
con solo la primera opción de arranque, avíseme si la segunda opciónprocessor.max_cstate
hizo lo que estaba documentado en los comentarios para que pueda actualizar esta respuesta.Finalmente, el último de los tres parámetros,
idle=poll
es un verdadero acaparador de energía. Deshabilitará C1 / C1E, lo que eliminará el último bit restante de latencia a expensas de un consumo de energía mucho mayor, así que utilícelo solo cuando sea realmente necesario. Para la mayoría, esto será excesivo, ya que la latencia C1 * no es tan grande. Usando mi aplicación de prueba ejecutándose en el hardware que describí en la pregunta original, la latencia pasó de 9 a 3 us. Sin duda, esta es una reducción significativa para las aplicaciones sensibles a la latencia (por ejemplo, comercio financiero, telemetría / seguimiento de alta precisión, adquisición de datos de alta frecuencia, etc.), pero puede que no valga la pena el impacto de energía eléctrica incurrido para la gran mayoría de aplicaciones de escritorio. La única forma de saberlo con certeza es perfilar la mejora de rendimiento de su aplicación vs.Actualizar:
Después de realizar pruebas adicionales con diferentes
idle=*
parámetros, he descubierto que el establecimientoidle
demwait
si es compatible con su hardware es una idea mucho mejor. Parece que el uso de lasMWAIT/MONITOR
instrucciones permite que la CPU ingrese C1E sin que se agregue una latencia notable al tiempo de activación del hilo. Conidle=mwait
, obtendrá temperaturas de CPU más frías (en comparación conidle=poll
), menos uso de energía y aún conservará las excelentes latencias bajas de un bucle inactivo de sondeo. Por lo tanto, mi conjunto recomendado actualizado de parámetros de arranque para la latencia de activación de subprocesos de CPU baja basado en estos hallazgos es:El uso de en
idle=mwait
lugar deidle=poll
también puede ayudar con el inicio de Turbo Boost (al ayudar a que la CPU se mantenga por debajo de su TDP [Thermal Design Power]) y el hyperthreading (para lo cual MWAIT es el mecanismo ideal para no consumir un núcleo físico completo mientras se está tiempo evitando los estados C superiores). Sin embargo, esto aún no se ha probado en las pruebas, lo que seguiré haciendo.Actualización 2:
La
mwait
opción inactiva se ha eliminado de los kernels 3.x más nuevos (gracias al usuario ck_ por la actualización). Eso nos deja con dos opciones:idle=halt
- Debería funcionar tan bien comomwait
, pero pruebe para asegurarse de que este sea el caso con su hardware. LaHLT
instrucción es casi equivalente a unaMWAIT
con la sugerencia de estado 0. El problema radica en el hecho de que se requiere una interrupción para salir de un estado HLT, mientras que una escritura en memoria (o interrupción) se puede usar para salir del estado MWAIT. Dependiendo de lo que use el Kernel de Linux en su bucle inactivo, esto puede hacer que MWAIT sea potencialmente más eficiente. Entonces, como dije, pruebe / perfil y vea si satisface sus necesidades de latencia ...y
idle=poll
- La opción de mayor rendimiento, a expensas de la energía y el calor.fuente
Quizás lo que se volvió más lento es futex, el componente básico de las variables de condición. Esto arrojará algo de luz:
luego
que mostrará los microsegundos tomados para las llamadas al sistema interesantes, ordenados por tiempo.
En el kernel 2.6.32
En el kernel 3.1.9
Encontré este informe de error de hace 5 años que contiene una prueba de rendimiento de "ping pong" que compara
Tuve que agregar
para compilar, lo que hice con este comando
En el kernel 2.6.32
En el kernel 3.1.9
Concluyo que entre el kernel 2.6.32 y 3.1.9 el cambio de contexto se ha ralentizado, aunque no tanto como se observa en el kernel 3.2. Me doy cuenta de que esto aún no responde a su pregunta, seguiré investigando.
Editar: Descubrí que cambiar la prioridad en tiempo real del proceso (ambos subprocesos) mejora el rendimiento en 3.1.9 para que coincida con 2.6.32. Sin embargo, establecer la misma prioridad en 2.6.32 lo ralentiza ... imagínate, lo investigaré más.
Aquí están mis resultados ahora:
En el kernel 2.6.32
En el kernel 3.1.9
fuente
También puede ver procesadores haciendo clic hacia abajo en procesos más recientes y kernels de Linux debido al controlador pstate que está separado de c-states. Entonces, además, para deshabilitar esto, usa el siguiente parámetro del kernel:
intel_pstate=disable
fuente