En este artículo describiré sencillas optimizaciones de rendimiento aplicables a la mayoría de aplicaciones multithread debido a las interacciones entre el Scheduler de Windows 10 y las CPU multicore.
Estas aplicaciones comprenden la inmensa mayoría del software:
- Compresores de archivos (WinRAR, 7Zip…)
- Software de edición de imagen, vídeo y audio (Adobe Photoshop, Audition…)
- Escaneos antimalware.
- Cargas ML, DL y AI.
- Cálculos matemáticos y simulaciones numéricas.
- Compilación.
- Y cualquier software ejecutado en multitarea.
Las CPUs multicore actuales de interés para este estudio integran desde 4 cores con SMT, es decir, ejecutan más de 8 threads simultáneos. Las pruebas para este artículo las he ejecutado en el siguiente sistema portátil con 8 cores y 16 threads:
- Windows 10 Professional 1803 x64
- CPU AMD Ryzen 7 1700 de 8 núcleos y 16 threads
- 8 cachés L2 512 KB y 2 L3 8 MB
- Frecuencia fijada a 3.4 GHz para los tests (repetitividad)
- Voltaje CPU 1.10 V @ 3.4 GHz
- 24 GB DDR4 @ 2400 (16 + 8 GB)
- SSD Sansung Evo 970 500 GB NVMe PCIex 3.0 4X
- Driver NVMe Samsung
- SSHD Seagate Firecuda 1 TB (HD Híbrido)
- GPU AMD Radeon RX580 (driver 18.9.3)
El scheduler de Windows 10
El funcionamiento del Scheduler de Windows 10 (el sistema encargado entre otras tareas de distribuir los threads a las CPUs lógicas) tiene un funcionamiento muy optimizado. No es perfecto pero realmente hace un mejor trabajo que las versiones anteriores de Windows (8.1, 8, 7…) y marca una evolución evidente en el manejo de cargas multithread.
Si lanzamos una carga de trabajo de 8 threads en un sistema como el de pruebas, capaz de ejecutar 16 simultáneamente, Windows 10 reacciona del siguiente modo:
Como vemos, Windows 10 distribuye dinámicamente los threads (en este caso 8) entre las CPUs lógicas disponibles (16), por ello observamos:
- Cargas muy elevadas en las CPU lógicas 1,5,7,8,11,13 y 14.
- Medias en CPUs 0,2,3,9 y 12.
- Uso de CPU casi nula en las CPUs lógicas 4,6,10 y 15.
Las razones de este comportamiento radican entre otras en eliminar los hotspots rotando las carga de trabajo entre las CPUs disponibles para que ningún core llegue a temperaturas extremas.
Lógicamente Windows actúa prudentemente, muchos sistemas, algunos de ellos ‘de marca’, están montados descuidadamente (siendo educado / positivo) y otras veces cuentan con sistemas de refrigeración claramente insuficientes.
Afinidad manual en el Administrador de Tareas de Windows 10
Claramente podemos mejorar los resultados.
Carga de enteros con afinidad manual de los 8 threads a las 8 CPUs lógicas (0, 2, 4, 6, 8, 10, 12 y 14 ), 8 threads en CPU 16 threads:
Lo que hacemos aquí es:
Posicionar cada thread en un núcleo físico reserva para él el 100% de los recursos del core.
Por ello, podemos ver las CPUs lógicas impares (1, 3, 5, 7, 9, 11, 13 y 15) prácticamente en reposo (sin carga de procesamiento).
Las Ventajas de Afinitizar Threads
Una mejora aproximada de un 10% en prestaciones.
Y lo que es más importante, una muy mejorada responsividad de la máquina. Esta mejora es objetiva (puede medirse) y tanto o más importante subjetivamente (sensaciones) el equipo parece muy superior.
Las razones que motivan esta mejora son:
- Hay 8 CPUs lógicas completamente libres (1, 3, 5, 7, 9, 11, 13 y 15) para aceptar tareas.
- Se dan muchos menos fallos de caché L1 y L2 (y en este caso la L0i μop cache de 2 Kμops que integran los cores AMD Zen) por no cambiar el código y los datos físicamente de core.
- Menor movimiento de datos entre las 2 cachés L3 de 8 MB (CCX0 – CCX1).
- Menos accesos a la DRAM dual channel DDR4 2400 debido a la mayor tasa de aciertos de caché.
- Se facilita la gestión de energía del procesador y del sistema operativo ya que producimos una carga constante y distribuida sin variaciones en el tiempo.
- Mejor consumo energético y menores temperaturas globales.
- Mayor duración de la batería.
También, en cargas más limitadas en número de threads resulta ventajoso afinitizar a los cores los threads aunque solo compensa cuando las cargas de trabajo llevan al menos varios minutos de proceso.
Yo lo utilizo extensivamente en tareas de compresión de datos con WinRAR o 7z y es un aliado en cualquier carga que lleve tiempo (rendering, tareas de red, …)
Yo a mi maquina un AMD FX 4100 en Win 7 se me ocurrió instalarle el programa Process Lasso y le asigne a los núcleos determinados procesos dependiendo del programa y hasta ahora me ha dado buen resultado.
Hernan, Process Lasso es un muy buen software para gestión avanzada de procesos y threads en Windows 10.
Las últimas versiones, además, incluyen optimizaciones para CPUs multicore masivas como el AMD Threadripper 2990WX con 32 cores y 64 threads que en cierto tipo de software tenían problemas de escalado.
Estos problemas eran debidos a un bug en el mscheduler de Windows 10:
https://www.linkedin.com/feed/update/urn:li:activity:6494498312090193920
Saludos,
Carlos Yus Valero.
Un gran avance Windows 10
Microsoft va mejorando paso a paso la arquitectura del Scheduler porque llegan al mercado diseños de CPU cada día más complejos.
Por ejemplo, un AMD EPYC 7601:
https://www.amd.com/es/products/cpu/amd-epyc-7601
Con 32 cores, 64 threads y 8 canales DDR4 es de cara al sistema operativo cuatro nodos NUMA. Es decir, hay 4 controladoras DDR4 dual channel independientes y que sirven cada una a 2 CCX de 4 cores / 8 threads.
En este caso, el scheduler de Windows 10 y Windows Server 2016 y 2019 todavía falla estrepitosamente:
https://www.anandtech.com/show/13853/amd-comments-on-threadripper-2-performance-and-windows-scheduler
Saludos,
Carlos Yus Valero.