En la última versión de DeVeDe añadí por fin soporte para multithreading en Mencoder. Por desgracia no es algo tan simple como decirle «Usa threads», sino que hay que decirle cuantos queremos usar.
Alguno dirá: «¡Pues tantos como núcleos tengamos en nuestro ordenador, por supuesto!»
Y efectivamente, esa es la respuesta. El problema viene cuando queremos saber cuantos núcleos tenemos en nuestro ordenador. La primera solución que consideramos es leer /proc/cpuinfo y contar el número de veces que aparece la palabra processor. Por desgracia la cosa no es tan sencilla por culpa de un infame invento de Intel: el HyperThreading. Esta tecnología lo que hace es simular dos núcleos en procesadores de un único núcleo.
Los procesadores actuales no siempre ejecutan las instrucciones en el mismo orden en que están escritas en la memoria, sino que pueden enviar antes algunas que están después, siempre y cuando no dependan de los resultados de ninguna instrucción anterior. Esto permite aprovechar mejor tiempos muertos en la ejecución de instrucciones (por ejemplo, el tiempo que tiene que esperar una instrucción de carga a que llegue un dato desde la memoria). Pues bien, la mayor independencia de instrucciones se da entre procesos diferentes: es posible entremezclar las instrucciones de dos procesos cualesquiera sin que haya riesgo de dependencia, precisamente porque son completamente independientes (valga la redundancia). Los ingenieros de Intel lo entendieron rápidamente y se les ocurrió que podrían llenar huecos en la ejecución de un proceso con instrucciones de otro, aprovechando así al máximo las distintas unidades funcionales del procesador. Desde fuera el procesador parecería tener dos núcleos, y así lo verían los sistemas operativos, cuando en realidad sólo habría uno. Esto permitía incluso no tener que modificar nada en el software.
Sobre el papel la idea parecía muy prometedora, y de hecho Intel afirmaba que se conseguían aumentos de rendimiento de hasta el 30%. Pero en la práctica no sólo era raro acercarse a dicha cifra, sino que había veces que las aplicaciones iban incluso más lentas. La razón no residía en el hyperthreading en sí, sino en su interacción con el Replay System de los procesadores Pentium 4 (que son los únicos que, hoy por hoy, incorporan HT). En X-bit labs explican muy bien en qué consiste, así que no lo repetiré aquí porque quedaría muy largo y peor explicado.
¿Y qué ocurre en el caso particular de Mencoder? En general la gente no se pone de acuerdo. Algunos aseguran que no hay ninguna mejora, pero que tampoco empeora, mientras que otros afirman que va peor si se utilizan multiples hilos. Ante la duda decidí tomar la opción conservadora, lo que implica calcular con precisión el número real de núcleos. Por desgracia no he encontrado una manera «oficial» de saber esa cantidad, sino que hay que deducirla de los datos que ofrece /proc/cpuinfo. Para entender bien la nomenclatura me referiré como procesador a cada uno de los chips que hay en un ordenador, como núcleo real a cada uno de los núcleos físicos de un procesador, y como núcleo virtual al número de núcleos que cree ver un sistema operativo. Así, en un sistema con dos procesadores, en el que cada uno hay dos núcleos, tendremos un total de cuatro núcleos reales. Si encima todos tienen HyperThreading, tendremos un sistema con ocho núcleos virtuales.
Estos son los cuatro parámetros importantes en un sistema con múltiples núcleos y/o procesadores:
- Physical id: identificador único de cada procesador (chip).
- Core id: identificador único de cada núcleo real dentro de un mismo procesador.
- Cpu cores: número de núcleos reales en este procesador.
- Siblings: número de núcleos virtuales en este procesador.
A la vista de estas definiciones podría parecer que basta con contar el número de combinaciones diferentes de Physical id y Core id para saber cuantos núcleos tenemos. Eso hice en la versión 3.8 de DeVeDe y el batacazo no tardó en llegar: un usuario con un procesador AMD Phenom (cuatro núcleos) se quejaba de que sólo usaba un núcleo.
Tras recibir una copia de su /proc/cpuinfo la sorpresa fue mayúscula: ¡las cuatro entradas processor tenían exactamente el mismo Physical id y Core id, y el número de cores en cada una era uno en lugar de cuatro!
Era necesaria una nueva aproximación al problema, así que recopilé las descripciones de todas las máquinas que pude, lo que complicó aún más la cosa porque los valores bailaban y diferían mucho de lo que sería lógico: en sistemas con dos núcleos Cpu cores vale 2, pero en el sistema con cuatro núcleos sólo vale 1. Lo mismo ocurría con el valor de Siblings. Probé varias aproximaciones, cada cual más complicada, hasta que encontré la solución: cada entrada en cpuinfo se corresponde con un núcleo virtual, así que basta con calcular a qué porcentaje de un núcleo real se corresponde. Ese valor lo obtenemos con el cociente entre Cpu cores y Siblings.
En efecto, en mi procesador Athlon X2 y en un Intel Core 2 Duo, Cpu cores y Siblings valen ambos 2 en cada entrada processor, con lo que 2/2=1. En el procesador Phenom de cuatro núcleos ambos valen 1 en cada entrada, con lo que 1/1=1. Pero en un procesador con HyperThreading Siblings vale 2, mientras que Cpu cores vale 1, con lo que 1/2=0,5. Cada entrada processor cuenta como medio núcleo en la máquina con HyperThreading, mientras que en el resto de sistemas cuenta como un núcleo.
¿Y qué pasa en una máquina con un sólo procesador de único núcleo? Pues que no aparece ninguna de esas dos entradas, por lo que hay que asumir que Cpu cores y Siblings valen 1 salvo que aparezcan en la descripción del procesador.
Así pues, basta con ir sumando el cociente entre Siblings y Cpu cores para cada una de las entradas para, al final, obtener el número de núcleos reales del sistema. Este es el sistema que usaré en DeVeDe 3.9, que saldrá dentro de unas semanas.