Gestión dinámica del rendimiento de CPUs bajo Linux

Esta semana hemos tenido la enorme suerte de poder impartir un seminario sobre seguridad para menores en Internet en el Colegio CEU San Pablo de Sevilla. Para ilustrar de forma práctica los conocimientos adquiridos en la sesión, instalamos una pequeña red formada por portátiles de un perfil antiguo (Intel Pentium M 1,6 GHz, 512MB RAM) en los que tenemos instalado Debian Linux con el escritorio XFCE (una pequeña maravilla que saca todo el jugo a equipos poco potentes).

Aparte de utilizar un escritorio ligero, es posible obtener el máximo rendimiento posible a portátiles tan antiguos gracias al escalado dinámico de la frecuencia de la CPU, una técnica que permite equilibrar el consumo de energía frente a la frecuencia del procesador según las necesidades del usuario. En Linux, esta gestión se realiza a través de los CPU governors, unos esquemas que indican al núcleo cómo debe llevar a cabo estos ajustes. En este artículo vamos a ver cómo realizar nosotros mismos esta gestión dinámica de la frecuencia de nuestras CPUs, que podemos aplicar a cualquier tipo de sistema, no sólo en portátiles.

Como es habitual en el resto de mis artículos, la plataforma Linux de referencia es Debian. Aún así, lo que cuento en las siguientes líneas se puede hacer igual o muy parecido en otras distribuciones populares. Evidentemente, vamos a trabajar directamente con el hardware, así que sólo el superusuario tiene los privilegios necesarios para lo que vamos a ver a continuación.

En primer lugar, hay que activar el soporte de la gestión dinámica de la frecuencia de la CPU. Para ello, si no lo está ya, instalamos el software necesario:

# apt-get install cpufrequtils
Leyendo lista de paquetes... Hecho
Creando árbol de dependencias
Leyendo la información de estado... Hecho
Se instalarán los siguientes paquetes extras:
libcpufreq0
Se instalarán los siguientes paquetes NUEVOS:
cpufrequtils libcpufreq0
...
Seleccionando el paquete libcpufreq0 previamente no seleccionado.
(Leyendo la base de datos ... 254034 ficheros o directorios instalados actualmente.)
Desempaquetando libcpufreq0 (de .../libcpufreq0_008-1_amd64.deb) ...
Seleccionando el paquete cpufrequtils previamente no seleccionado.
Desempaquetando cpufrequtils (de .../cpufrequtils_008-1_amd64.deb) ...
Procesando disparadores para man-db ...
Configurando libcpufreq0 (008-1) ...
Configurando cpufrequtils (008-1) ...
[ ok ] Loading cpufreq kernel modules...done (powernow-k8).
[ ok ] CPUFreq Utilities: Setting ondemand CPUFreq governor...CPU0...CPU1...done.

Como vemos en el ejemplo, la plataforma hardware que uso en el ejemplo tiene la arquitectura AMD64 y cuenta con dos procesadores (realmente es sólo uno pero con dos núcleos), que son detectados automáticamente al terminar la instalación del software.

Toda la gestión ser realiza a través del directorio /sys/devices/system/cpu/cpuX/cpufreq, donde X es el número de orden la CPU que queremos gestionar. Veamos lo que nos encontramos para el primer procesador:

# cd /sys/devices/system/cpu/cpu0/cpufreq
# ls -l
total 0
-r--r--r-- 1 root root 4096 may 30 affected_cpus
-r--r--r-- 1 root root 4096 may 30 bios_limit
-r-------- 1 root root 4096 may 30 cpuinfo_cur_freq
-r--r--r-- 1 root root 4096 may 30 cpuinfo_max_freq
-r--r--r-- 1 root root 4096 may 30 cpuinfo_min_freq
-r--r--r-- 1 root root 4096 may 30 cpuinfo_transition_latency
-r--r--r-- 1 root root 4096 may 30 related_cpus
-r--r--r-- 1 root root 4096 may 30 scaling_available_frequencies
-r--r--r-- 1 root root 4096 may 30 scaling_available_governors
-r--r--r-- 1 root root 4096 may 30 scaling_cur_freq
-r--r--r-- 1 root root 4096 may 30 scaling_driver
-rw-r--r-- 1 root root 4096 may 29 scaling_governor
-rw-r--r-- 1 root root 4096 may 30 scaling_max_freq
-rw-r--r-- 1 root root 4096 may 30 scaling_min_freq
-rw-r--r-- 1 root root 4096 may 30 scaling_setspeed
drwxr-xr-x 2 root root 0 may 30 stats

Se puede apreciar que, excepto stats que es un directorio, el resto de contenido son ficheros que se pueden consultar y modificar para realizar la gestión dinámica de la frecuencia. Veamos qué governors tenemos disponibles, cuál es el que está actualmente configurado y activo en este procesador y cuál es su frecuencia actual (en Hz):

# cat scaling_available_governors
userspace powersave conservative ondemand performance

# cat scaling_governor
ondemand

# cat cpuinfo_cur_freq
1000000

Es decir, que el procesador está configurado para que modifique su frecuencia de forma dinámica según las necesidades del sistema en cada momento (el governor ondemand). Muy parecido es el governor conservative, que cambia la frecuencia de forma más gradual en vez de saltar directamente a la velocidad de CPU necesaria, lo que lo hace más idóneo para sistemas alimentados por batería. Los governors performance y powersave configuran la CPU a la máxima y mínima frecuencia de forma estática, respectivamente, y el governor userspace permite que los usuarios y procesos con privilegios de superusuario puedan modificar dinámicamente la velocidad del procesador.

Supongamos que queremos utilizar el governor performance. Nada más sencillo:

# echo performance > scaling_governor

# cat scaling_governor
performance

# cat cpuinfo_cur_freq
2200000

Ya tenemos la CPU a pleno rendimiento (y gastando más energía, claro).

En el directorio /sys/devices/system/cpu/cpuX/cpufreq tenemos más ficheros para consultar más datos sobre nuestras CPUs y alterar su comportamiento, pero también podemos hacerlo a través de una sencilla herramienta de línea de comandos:

# cpufreq-info
cpufrequtils 008: cpufreq-info (C) Dominik Brodowski 2004-2009
Report errors and bugs to cpufreq@vger.kernel.org, please.
analyzing CPU 0:
driver: powernow-k8
CPUs which run at the same hardware frequency: 0 1
CPUs which need to have their frequency coordinated by software: 0 1
maximum transition latency: 109 us.
hardware limits: 1000 MHz - 2.20 GHz
available frequency steps: 2.20 GHz, 2.00 GHz, 1.80 GHz, 1000 MHz
available cpufreq governors: userspace, powersave, conservative, ondemand, performance
current policy: frequency should be within 1000 MHz and 2.20 GHz.
The governor "performance" may decide which speed to use
within this range.
current CPU frequency is 2.20 GHz (asserted by call to hardware).
cpufreq stats: 2.20 GHz:18,75%, 2.00 GHz:2,63%, 1.80 GHz:15,07%, 1000 MHz:63,55% (3424)
analyzing CPU 1:
driver: powernow-k8
CPUs which run at the same hardware frequency: 0 1
CPUs which need to have their frequency coordinated by software: 0 1
maximum transition latency: 109 us.
hardware limits: 1000 MHz - 2.20 GHz
available frequency steps: 2.20 GHz, 2.00 GHz, 1.80 GHz, 1000 MHz
available cpufreq governors: userspace, powersave, conservative, ondemand, performance
current policy: frequency should be within 1000 MHz and 2.20 GHz.
The governor "performance" may decide which speed to use
within this range.
current CPU frequency is 2.20 GHz (asserted by call to hardware).
cpufreq stats: 2.20 GHz:18,75%, 2.00 GHz:2,63%, 1.80 GHz:15,07%, 1000 MHz:63,55% (3424)

Mediante cpufreq-info -h podemos consultar la lista de parámetros disponibles para poder elegir la información concreta que queremos obtener en cada momento. También contamos con las utilidades cpufreq-set para modificar el comportamiento de la CPU y cpufreq-aperf para calcular la frecuencia media en un periodo de tiempo (atención: no todas las CPUs admiten esta consulta). Ambas herramientas admiten el parámetro -h para obtener la lista de opciones que ofrecen.

Sin embargo, todos los cambios que hagamos se pierden en el momento en que reiniciemos el equipo. Por tanto hay que utilizar un mecanismo que nos permita mantener nuestras modificaciones entre reinicios. Si sólo queremos establecer un governor concreto, basta con que lo especifiquemos en el fichero /etc/default/cpufrequtils (si no existe, lo creamos) insertando el siguiente contenido:

GOVERNOR="performance"

Si queremos modificar otros parámetros de la CPU, como la frecuencia de inicio, es necesario instalar el paquete sysfsutils:

# apt-get install sysfsutils
Leyendo lista de paquetes... Hecho
Creando árbol de dependencias
Leyendo la información de estado... Hecho
Se instalarán los siguientes paquetes extras:
libsysfs2
Se instalarán los siguientes paquetes NUEVOS:
libsysfs2 sysfsutils
...
Seleccionando el paquete libsysfs2:amd64 previamente no seleccionado.
(Leyendo la base de datos ... 254004 ficheros o directorios instalados actualmente.)
Desempaquetando libsysfs2:amd64 (de .../libsysfs2_2.1.0+repack-2_amd64.deb) ...
Seleccionando el paquete sysfsutils previamente no seleccionado.
Desempaquetando sysfsutils (de .../sysfsutils_2.1.0+repack-2_amd64.deb) ...
Procesando disparadores para man-db ...
Configurando libsysfs2:amd64 (2.1.0+repack-2) ...
Configurando sysfsutils (2.1.0+repack-2) ...
[ ok ] Setting sysfs variables....

y modificar el fichero /etc/sysfs.conf adecuadamente. Dicho fichero incluye suficientes ejemplos como para que sea sencilla la tarea de incluir nuestras propias modificaciones. Tras guardar los cambios, los activamos con /etc/init.d/sysfsutils start.

En definitiva, gracias a los governors los administradores de sistemas Linux contamos con una herramienta potente y flexible para optimizar el rendimiento de uno de nuestros recursos más preciados: las CPUs de los sistemas a nuestros cargo.