Вы находитесь на странице: 1из 14

Mejorando la Performance en Sistemas Linux/Unix

Diego Bravo E.
9 de Octubre de 2005

Tabla de contenidos
1. Introduccin................................................................................................................2 2. Higiene.........................................................................................................................2 3. Conguracin de la Memoria RAM.......................................................................4 4. Anlisis CPU-I/O .......................................................................................................7 A. Ejercitador de memoria..........................................................................................13

Se proporciona una metodologa prctica para mejorar la performance de sistemas Linux, la cual puede ser aplicable a variantes Unix. Cualquier sugestin o correccin, favor escribir a diego_bravo_estrada at yahoo dot com1.

1. Introduccin
Este es un texto breve con algunas sugestiones acerca de cmo mejorar la performance de un sistema Linux (o Unix.) No pretende cubrir el material que se detalla en los libros correspondientes, sino tan solo ser una primera aproximacin para quien tiene inters en el particular y desea una explicacin rpida. Se propone la siguiente metodologa secuencial para mejorar la performance: 1. Eliminar los factores negativos que deterioran la performance (higiene) 2. Analizar y recongurar la memoria de ser necesario 3. Analizar y recongurar la relacin %CPU vs %I/O

1.1. Autora y Copyright


Este documento tiene copyright (c) 2006 Diego Bravo Estrada <diegobravoestrada en hotmail>. Se otorga permiso para copiar, distribuir y/o modicar este documento bajo los trminos de la "GNU Free Documentation License, Version 1.2", excepto en lo mencionado en el siguiente prrafo. Esta licencia puede obtenerse en: http://www.gnu.org/licenses/fdl.txt Si se desea crear un trabajo derivado o publicar este documento para cualquier propsito, apreciara que se me contacte primero a n de tener la oportunidad de proporcionar una versin ms reciente. De no ser esto posible, la ltima versin debera estar disponible en el sitio web GatoGringo.com.3

2. Higiene
Asumiendo que nuestro sistema puede mejorar su performance (en la mayora ocurre as), lo primero es corregir ciertos problemas tpicos. En particular:

Eliminar procesos "pesados" intiles Re-escribir procesos "pesados" tiles

Llamaremos "pesado" a un proceso que torna lento al sistema. Esto ocurre comunmente porque aqul consume muchos recursos de procesamiento (mucho CPU) o consume mucha memoria.

2.1. Consumo excesivo de CPU de un proceso


Muchas veces un proceso que est fuera de control (por un error en su programacin) entra en loops ilimitados que consumen inutilmente el CPU; en otros casos, esto ocurre de manera "normal" durante la ejecucin de un proceso frecuente4. Ms all de analizar el por qu ocurre esto (que es ms responsabilidad del programador), preocupmonos por detectarlo. Para esto, la manera ms sencilla quiz sea emplear el comando:
# ps axu

(Consltese el manual de ps para una explicacin de sus opciones.) En particular, estamos interesados en la columna que reza "%CPU". Esta columna proporciona 2

Mejorando la Performance en Sistemas Linux/Unix el porcentaje que representa el tiempo de CPU consumido por el proceso respecto al tiempo total de su ejecucin. El siguiente programa se ejecuta durante 20 segundos. Los 10 primeros el proceso no consume CPU, y luego inicia un loop sin descanso hasta que transcurran otros 10 segundos. Ms abajo se muestra el monitoreo del mismo en diversos momentos para un sistema sin carga:
#include <stdio.h> #include <unistd.h> #include <time.h> #define DELAY 10 main() { volatile int z; time_t t; /* dormir DELAY segundos */ sleep(DELAY); /* loop infinito durante DELAY segundos */ t=time(NULL); for(z=0;;z++) if(time(NULL)>=t+DELAY) break; return 0; }

Ejemplo de uso5:
$ ./sleep_and_run & [1] 1194 $ ps axu|head -1 USER PID %CPU %MEM VSZ RSS $ ps axu|grep sleep_and_ru[n] diego 1141 0.0 0.0 1376 252 $ ps axu|grep sleep_and_ru[n] diego 1141 0.0 0.0 1376 252 $ ps axu|grep sleep_and_ru[n] diego 1141 31.6 0.0 1376 256 $ ps axu|grep sleep_and_ru[n] diego 1141 49.8 0.0 1376 256 $ ps axu|grep sleep_and_ru[n] diego 1141 51.2 0.0 1376 256 $ ps axu|grep sleep_and_ru[n] $

STAT START S S R R R 22:01 22:01 22:01 22:02 22:01

TIME COMMAND 0:00 ./sleep_and_run 0:00 ./sleep_and_run 0:02 ./sleep_and_run 0:05 ./sleep_and_run 0:06 ./sleep_and_run

Como se aprecia, la columna %CPU empieza a crecer tras unos momentos hasta alcanzar un valor signicativo (51.2%.) Lamentablemente, este procedimiento no detecta los procesos que de pronto salen de control si stos ya se han venido ejecutando durante mucho tiempo manteniendo un consumo moderado de CPU (pues en ese caso el %CPU tardar mucho en hacerse signicativo.)6 Afortunadamente, existe otra forma muy sencilla de analizar este caso, y consiste en vericar la columna TIME del proceso en diversos momentos. Si se aprecia el ejemplo anterior, se observa que la columna TIME se mantiene estable (en este caso, con 0:00) durante los primeros momentos, y luego rpidamente se incrementa. Esto es un indicativo claro de que el proceso est consumiendo mucho de nuestro CPU. En este punto, el administrador deber matar inmediatamente al proceso, si ste resulta ser intil. En cambio, si el proceso cumple alguna funcin de utilidad en el sistema, se deber esperar un tiempo prudencial para que vuelva a la normalidad (deje de consumir tanto CPU) o para que termine.

Mejorando la Performance en Sistemas Linux/Unix

2.2. Consumo excesivo de Memoria de un proceso


A veces asociado con lo anterior, los procesos pueden consumir demasiada memoria por distintos motivos:

Errores de programacin, especialmente al no liberar los objetos La memoria fsica es insuciente para la cantidad de informacin que se requiere utilizar

Una manera rpida de saber si el sistema en su conjunto tiene carencia de memoria fsica consiste en analizar las columnas si y so bajo --swap--7:
$ vmstat 2 procs -----------memory---------- ---swap-- -----io---r b swpd free buff cache si so bi bo 0 0 0 321408 11960 95300 0 0 36 9 0 0 0 321424 11960 95300 0 0 0 4 0 0 0 321424 11960 95300 0 0 0 0

Si los valores en las columnas mencionadas son cero (quiz con pequeas excepciones espordicas), quiere decir que el sistema tiene suciente memoria fsica para los requerimientos DE ESTE MOMENTO. Por lo tanto, el administrador har bien en analizar la salidad de vmstat en diferentes momentos del da, o cuando se ejecutan los procesos cuya lentitud nos preocupa ms. Con el n de analizar la memoria consumida por los procesos, podemos referirnos a las columnas %MEM y RSS de la salida nuestro conocido ps axu, que son equivalentes. RSS corresponde a la memoria que el sistema operativo ha otorgado para el proceso, el cual se presenta como porcentaje respecto al total en %MEM. Siguiendo la idea, es una buena idea analizar qu procesos consumen mucha memoria y desactivarlos si no son imprescindibles. Ms abajo se explica con detalle qu se puede hacer si tenemos un sistema con carencia de memoria en el que no podemos desactivar los procesos culpables.

2.3. Re-escritura de procesos pesados


No pretendo entrar al tpico de optimizar la ejecucin de los programas. En mi opinin, los buenos programadores casi siempre pueden optimizar la ejecucin de sus programas para que consuman menos CPU o Memoria, aunque no es una tarea trivial que conlleve poco tiempo. El problema aqu no es tcnico, sino de "gerencia": el programador suele ser obligado a liberar una versin operativa que rpidamente pasa a produccin, y luego se le reasigna a otra actividad sin darle tiempo para (ni obligarle a) que la optimice. Los jefes de IT se sorprenderan gratamente si asignaran a sus buenos programadores exclusivamente a optimizar las aplicaciones por algunos das. Un aspecto imprescindible de resaltar porque resulta evidente, es el consumo excesivo de recursos que hacen los programas desarrollados en lenguajes tipo scripting como el Shell. Si bien es cierto que programar en el Shell (o similares) tiene mltiples ventajas, se debe tener en cuenta que una aplicacin equivalente reescrita en lenguaje C suele ser de 10 a mil veces ms veloz, y por tanto mucho menos derrochadora de CPU y Memoria. El shell tiene su lugar para diversas tareas administrativas y para prototipado de cierta clase de aplicaciones, pero rara vez debera constituirse en el lenguaje de una aplicacin principal. Como se sabe, muchas veces ocurre que la falta de tiempo o la desidia hace que la gente utilice el prototipo y postergue (para siempre) el desarrollo "en serio".

Mejorando la Performance en Sistemas Linux/Unix

3. Conguracin de la Memoria RAM


Como se indic en la introduccin, una vez que se ha aplicado las normas de "higiene" en el sistema, tenemos dos posibilidades para mejorar an ms su performance: la conguracin de la memoria y la reparticin CPU-I/O. Aunque ambos anlisis se pueden hacer en paralelo, es muy conveniente hacerlos secuencialmente a n de cuanticar separadamente el impacto y simplicar las observaciones. Asimismo, es bastante recomendable empezar con la conguracin de la memoria, dado que las soluciones por este concepto son ms sencillas y menos costosas que en lo concerniente a la reparticin CPU-I/O.

3.1. Sistemas sin carencia de memoria


Algunos sistemas nunca tienen absolutamente ninguna carencia de memoria. Una forma sencilla de determinar esta situacin es observando si han hecho uso del swap. Como se aprecia en el ejemplo, el swap (en este caso de aproximadamente 1200 megabytes) no tiene uso HASTA ESTE MOMENTO, lo que signica que el sistema todava no ha tenido que recurrir al l por falta de memoria:
sys1$ /sbin/swapon -s Filename Type /dev/hda7 partition Size Used 1228932 0 Priority -1

Si esta situacin se mantiene a lo largo de muchos das, es indicativo de que dicho sistema probablemente tiene exceso de memoria, o cuando menos, exceso de swap. En estos sistemas, dicilmente podramos mejorar la performance en lo referente al consumo de memoria.

3.2. Rgimen de carencia de memoria


Normalmente los sistemas experimientan carencias de memoria durante ciertos lapsos en los que se ejecutan determinados procesos pesados. Si estos "perodos de carencia" son breves, entonces no hay mucho que mejorar en cuanto a la falta de memoria. Por el contrario, si los perodos de carencia son prolongados, la performance se puede multiplicar asombrosamente. En conclusin, el primer paso consiste en determinar si nuestro sistema tiene largos perodos de carencia de memoria (por lo menos de algunos minutos) o si stos son breves y poco numerosos (de pocos segundos de duracin.) Para el primer caso se aconseja una reconguracin, mientras que para el segundo una reconguracin mejorara muy poco la performance del conjunto y probablemente no valdra el esfuerzo.

3.3. Observacin de la carencia de memoria


A continuacin se muestra parte de la salida de vmstat para un sistema que inicialmente no tiene carencias de memoria, pero luego de unos segundos s que las tiene:
$ vmstat 2 ---------memory- ---swap-swpd free si so 67288 478472 188 262 67288 478472 0 0 67288 478472 0 0 67288 478472 0 0 67288 478456 0 0 67236 102320 34 0 72148 3336 1276 4202 111640 2960 1674 20072 129996 3208 1960 9180 129996 3568 3200 0 129996 3176 2454 0 129988 3496 3672 0 -----io---- --system-- ----cpu---bi bo in cs us sy id wa 282 279 1037 239 8 1 81 10 0 0 1002 59 2 0 99 0 0 0 1001 54 2 0 99 0 0 24 1010 103 1 0 99 0 0 0 1017 467 8 1 91 0 46 16 1023 297 11 54 35 2 2446 4204 1424 459 15 23 0 61 4210 20086 1263 273 1 6 0 93 3622 9180 1190 327 1 4 0 96 3862 0 1245 480 1 2 0 97 2984 6 1225 414 4 2 0 95 4076 68 1328 567 2 2 0 96

Mejorando la Performance en Sistemas Linux/Unix


129988 175224 175224 76508 76508 76508 76508 76508 2892 2768 3016 474376 474388 474404 474408 474408 3268 0 4396 2 1325 2864 22664 3186 22710 1309 2460 0 3030 0 1254 438 0 478 0 1060 6 0 6 2 1012 0 0 0 0 1001 0 0 0 0 1001 0 0 0 0 1001 531 455 675 189 436 54 57 51 0 2 0 97 1 7 0 92 5 3 0 92 2 10 70 20 8 1 91 1 2 0 99 0 1 0 99 0 2 0 99 0

Obviando la primera lnea (que son valores acumulados), apreciamos que bajo las columnas si y so (swap in/swap out) los valores permanecen inicialmente en cero, y luego so se dispara; luego de unos instantes si tambin adquiere valores signicativos. Finalmente tras unos 20 segundos, ambas columnas retornan a cero. Este es un ejemplo tpico de un sistema que durante unos segundos experimienta carencia de memoria. Los usuarios de este sistema probablemente experimentarn lentitud en la respuesta durante estos momentos. Qu debemos hacer al respecto? si durante todo el da ste es el nico momento de carencia de memoria, entonces cualquier reconguracin de memoria slo nos permitira ganar hasta 20 segundos en todo el mismo da, lo cual es evidentemente despreciable. Si por el contrario, este comportamiento hubiera durado algunas horas, o si ocurriera en muchas ocasiones (que acumulando su duracin podra totalizar horas) entonces la reconguracin s es aplicable.

3.4. Reconguracin de la memoria


Si hemos decidido recongurar la memoria a partir del anlisis anterior, entonces tenemos esencialmente tres opciones: 1. Modicar el orden de ejecucin de los procesos 2. Modicar los programas 3. Ampliar la memoria Describiremos cada caso por separado.

3.4.1. Modicar el orden de ejecucin de los procesos


Este es el primer nivel de solucin al problema de la carencia de memoria; es el ms sencillo y el que requiere menos inversin. A modo de ejemplo, considrese un sistema en el cual dos procesos que se ejecutan separadamente uno detrs del otro tardan (en total) cinco minutos en completarse; pero si se ejecutan simultneamente tardan una hora o ms en terminar. Esto ocurre as debido a que cada proceso por separado no introduce al sistema en el rgimen de carencia de memoria o lo hace por muy poco tiempo e intensidad, mientras que la combinacin de ambos procesos s lo hace de lleno y la performance se reduce drsticamente. Este problema ocurre tpicamente en sistemas en los que el nmero de procesos es muy elevado y por simplicidad para los operadores, se preere la ejecucin simultnea. Evidentemente, la solucin consiste en identicar las "combinaciones" que generan el rgimen de carencia de memoria y serializar la ejecucin.

3.4.2. Modicar los programas


Las consideraciones acerca de la optimizacin que se comentaron en la seccin de "higiene" son totalmente aplicables aqu. Lamentablemente muchas instalaciones ejecutan programas de terceros o no cuentan con el personal adecuado para este trabajo, por lo que slo pueden recurrir a la ampliacin de la memoria fsica (ver ms abajo.)

Mejorando la Performance en Sistemas Linux/Unix En muchos casos (especialmente programas no documentados) este procedimiento puede resultar costoso (en tiempo de honorarios de programacin), poco seguro (pues tal vez el programa ya no se puede optimizar mucho en cuanto a su consumo de memoria), y lento (pues la optimizacin puede resultar complicada y requiere muchas horas de anlisis) por lo que tambin se recurre a la ampliacin de la memoria fsica.

3.4.3. Ampliar la memoria fsica


En este caso slo hay que determinar cunta memoria se va a agregar. En la mayora de casos la gente opta por agregar "bancos" de memoria que suelen duplicar la memoria existente, y volver a efectuar el anlisis de la performance para cerciorarse de que ya no hay carencia de aquella. Esta aproximacin iterativa es vlida, pero en algunos casos es aconsejable tener una idea ms exacta (por ejemplo, si el dinero escasea, o si no hay disponibilidad de "bancos" de memoria en el mercado, o si nuestro hardware est casi al lmite de su capacidad de crecimiento en memoria.) Durante la operacin del sistema es frecuente que se observen diversos episodios de carencia de memoria de poca importancia, pero que necesariamente irn utilizando nuestro swap. Si volvemos a la salida de vmstat del ejemplo anterior, apreciaremos que antes de que se inicie el perodo crtico el consumo de swap era de 67288 bloques (tambin se pudo obtener con swapon -s.) Este consumo se eleva hasta 175224 bloques para luego disminuir gradualmente. Esto quiere decir que el proceso pesado excedi en 175224-67288=107936 bloques la capacidad de nuestra memoria empleando el swap. Por tanto, si agregamos precsamente esta cantidad de memoria fsica (por ejemplo, un "banco" de 128Mb) el proceso pesado podr ejecutarse enteramente en RAM8. Evidentemente este tipo de anlisis requiere bastante paciencia y los resultados pueden ser variables de un da para otro en funcin de la carga total del sistema.

4. Anlisis CPU-I/O
Pregunta motivadora: Si dos procesos toman normalmente dos horas en ejecutarse, y un buen da adquirimos un CPU (quiz con la memoria respectiva) que proporciona el DOBLE de velocidad de procesamiento, cunto tardarn ahora estos procesos? La respuesta, evidentemente, NO es una hora.

4.1. Recursos consumidos por los procesos


Los procesos que se ejecutan en los computadores hacen uso de diversos subsistemas del computador, tales como el CPU, la Memoria, los discos, y quiz la red. Normalmente se asume que la memoria tiene una velocidad "compatible" con la del CPU9, por lo que los tiempos consumidos en ambos se combinan en uno slo: el "tiempo de CPU". Si un proceso no est ejecutando instrucciones del CPU en un momento dado, normalmente es porque est accediendo al disco o a la red (coloquialmente, se dice que est efectuando I/O.) Existe una tercera posibilidad que corresponde a procesos que "pausan" o "esperan eventos" sin hacer nada ms (estado "idle" u "ocioso".)

4.2. Reparticin CPU-I/O-Idle


A partir de lo anterior, para cualquier proceso se puede plantear que el tiempo de su ejecucin se divide en tres componentes:
Tiempo Total = Tiempo CPU + Tiempo I/O + Tiempo Idle

Mejorando la Performance en Sistemas Linux/Unix Esta relacin se puede acumular a todos los procesos del sistema y expresarla porcentualmente para un lapso de tiempo determinado:
% CPU sistema + % I/O sistema + % Idle sistema = 100%

El comando vmstat en sus ltimas columnas nos proporciona estos valores. Por ejemplo:
$ vmstat 5 procs ------ ---swap-- -----io---r b cache si so bi bo 2 0 110144 0 0 20 7 0 1 120520 0 0 2166 21 6 0 133916 0 0 2573 133 0 1 201372 0 0 13646 197 0 1 210380 0 0 1870 201 0 0 212548 0 0 437 163 --system-in cs 1013 237 1160 1424 1168 1311 1155 1227 1096 1305 1162 1049 ----cpu---us sy id wa 4 1 94 1 12 4 40 45 31 4 27 38 18 6 22 53 62 4 0 33 16 2 77 5

La primera la generada por vmstat debe ser descartada para este anlisis pues corresponde a un acumulado desde que el sistema fue encendido. Nuestro %CPU del sistema corresponde a la suma de las columnas us y sy (que se comentan luego.) El %I/O se encuentra en la columna wa, y por ltimo, el %Idle corresponde a la columna id. NOTA: El manual de vmstat indica que para kernels Linux de versin menor a 2.5.41, la columna wa siempre reporta cero, y la columna id contiene la suma de %Idle+%I/O10. El comando vmstat acepta, entre otros, un argumento numrico que corresponde al intervalo de muestreo (en segundos.) Si deseamos conocer la distribucin de los tiempos del sistema durante la ejecucin de ciertos procesos, debemos lanzar vmstat en paralelo con los mismos y promediar los valores obtenidos mientras stos se ejecutan. A modo de ilustracin, asumiremos que pretendo analizar la distribucin de tiempos de un proceso que tarda aproximadamente dos horas; vmstat se lanza con intervalo de muestreo de 20 minutos (1200 segundos):
$ vmstat 1200 procs ------- ---swap-- -----io---- --system-r b cache si so bi bo in cs 2 0 212640 0 0 31 7 1013 235 2 0 220324 0 0 3844 296 1021 2360 0 0 223900 0 0 1800 0 1087 1692 4 0 224144 0 0 0 0 1205 3511 1 0 226120 0 0 972 950 1223 2169 1 0 229008 0 0 1490 0 1070 2471 0 0 229916 0 0 564 320 1243 3014 ----cpu---us sy id wa 4 1 94 1 82 10 0 9 38 4 45 13 74 12 13 0 34 6 26 33 52 8 19 21 52 10 6 31

Descartando la primera la, los tiempos obtenidos son: Tabla 1. %CPU 92 42 86 40 60 62 9 13 0 33 21 31 %I/O 0 45 13 26 19 6 %IDLE 101 100 99 99 100 99 %TOTAL

No es necesario preocuparse porque el total no suma exactamente 100%. A continuacin el promedio de las columnas relevantes11:

Mejorando la Performance en Sistemas Linux/Unix Tabla 2. %CPU 63.7 17.8 %I/O 18.2 %IDLE

Esta distribucin es extremadamente til para averiguar en dnde est concentrada la "lentitud". En este ejemplo, claramente se aprecia que el %CPU es el factor principal, pero tanto el %I/O como el %Idle son signicativos. Por lo tanto, el diagnstico es: Mejorar todos los subsistemas, pero especialmente el CPU. Asumiendo que el proceso ha tomado dos horas (120 minutos) en total, de acuerdo a la tabla anterior los tiempos absolutos han sido: Tabla 3. T. CPU 76.4 min 21.4 min T. I/O 21.8 min T. IDLE

4.2.1. La pregunta inicial


Con los valores de la ltima tabla, podemos responder a la pregunta con que se inici esta seccin: Cunto tardarn en total los procesos si el CPU es el doble de veloz? Evidentemente, si el CPU es el doble de veloz, el tiempo en que ste es empleado se reducir a la mitad. Para nuestro caso:
T. CPU = 76.4 min / 2 = 38.2 min

Con lo que nuestros tiempos resultan siendo: Tabla 4. T. CPU 38.2 min 21.4 min T. I/O 21.8 min T. IDLE

Finalmente, el tiempo total de los procesos ser:


T. Total = 38.2 min + 21.4 min + 21.8 min = 1h 21min

En general, si el tiempo normal que tardan los procesos es To, y el CPU mejora en un factor F (en nuestro ejemplo, F=2), entonces el nuevo tiempo total Tn ser:
Tn = To [1-(%CPU/100).(1-1/F)]

Para nuestro ejemplo:


Tn = 120 [1-(63.7/100).(1-1/2)] = 81.78 min = 1h 21min

Evidentemente este mismo anlisis se puede aplicar para el subsistema de I/O, correspondiendo al incremento (o disminucin) de la velocidad de los discos, o en ocasiones al incremento de la memoria fsica que permite el crecimiento del cach de disco. Finalmente, tngase en cuenta que en Linux, el tiempo de espera con la red se considera tiempo Idle.

Mejorando la Performance en Sistemas Linux/Unix

4.2.2. User Time y System Time


Anteriormente (en la salida de vmstat) se explic que el %CPU estaba constituido por la adicin de los valores us (User Time) y sy (System Time.) Estos valores pueden ser tiles cuando se pretende optimizar el cdigo de un programa a n de que consuma menos tiempo de CPU. El "User Time" corresponde al tiempo que el CPU invierte en ejecutar el texto de los programas en cuestin (clculos, toma de decisiones, acceso a memoria), mientras que el "System Time" es el tiempo que el CPU invierte en ejecutar cdigo del kernel en funcin de las solicitudes que el programa le hace (system calls.) La observacin de estos valores puede hacerse para un proceso particular (pues vmstat obtiene el promedio de todo el sistema) mediante el comando time. Por ejemplo:
$ time find /etc > /dev/null find: /etc/rcS.d: Permission denied find: /etc/ssl/private: Permission denied find: /etc/amanda: Permission denied real 0m4.077s user 0m0.003s sys 0m0.050s $ $ time find /etc > /dev/null find: /etc/rcS.d: Permission denied find: /etc/ssl/private: Permission denied find: /etc/amanda: Permission denied real user sys 0m0.047s 0m0.006s 0m0.018s

Este ejemplo muestra que el comando especicado (find) en su primera ejecucin tard en total 4.077 segundos en completarse. Durante este tiempo, slo tres milsimas correspondieron a la ejecucin del cdigo del programa (pues find no hace mucho procesamiento de informacin), y cincuenta milsimas fueron dedicadas al cdigo del kernel (en el caso de find, este cdigo del kernel se encarga de navegar en una estructura de directorios.) Ntese que el tiempo total de CPU corresponde a slo 53 milsimas: muy lejos de los cuatro segundos totales. Esto se debe a 1) otros procesos tambin se estn ejecutando, y 2) el proceso tiene que esperar a que el disco entregue la informacin solicitada, lo cual es varios rdenes de magnitud ms lento que las operaciones de CPU. En la segunda ejecucin el tiempo total se reduce radicalmente a 47 milsimas (ochenta y seis veces ms rpido), lo cual se debe sin duda a que los datos de la primera ejecucin estn ahora en el cach de disco y ya no hay necesidad de ir al disco fsico. Ntese que los tiempos de CPU tambin han cambiado, quiz por el efecto de otros procesos simultneos, y (para el caso del Kernel/System Time) porque el mecanismo de acceso a la informacin de la memoria cach resulta ms sencillo que el de un acceso real al disco fsico12. El conocimiento de esto nos puede dar pistas con respecto a la estrategia de optimizacin de un proceso pesado. Por ejemplo, para el caso anterior, es evidente que no sirve de mucho tratar de optimizar el (escazo) procesamiento que lleva a cabo find, sino enfocar los esfuerzos a emplear menos tiempo en el Kernel. Si bien no trataremos de reprogramar el Kernel, al menos s podemos tratar de reducir las llamadas que hacemos al mismo desde nuestro programa, o buscar otras llamadas ms ecientes. A modo de ejemplo, el siguiente programa ejecuta un clculo (intil) durante cinco segundos, y reporta cuantos millones de iteraciones se ha realizado dicho clculo:
#include <stdio.h> #include <sys/time.h> #include <time.h> int main(void)

10

Mejorando la Performance en Sistemas Linux/Unix


{ int x=1, y=2, z=3; long iter1=0,iter2=0; struct timeval tv1,tv2; gettimeofday(&tv1,NULL); for(;;) { x=(x*3+y*7+z*9)%11; y=(x*9+y*11+z*3)%29; z=(x*17+y*13+z*11)%37; iter1++; if(iter1==1000000) { iter2++; iter1=0; } gettimeofday(&tv2,NULL); if(tv2.tv_sec==tv1.tv_sec+5 && tv2.tv_usec>=tv1.tv_usec || tv2.tv_sec>tv1.tv_sec+5) break; } printf("Iteraciones: %ldM Resultado: %d %d %d\n",iter2,x,y,z); return 0; }

Por ejemplo, en mi computador obtuve:


$ time ./calculo1 Iteraciones: 12M Resultado: 6 4 5 real user sys 0m5.002s 0m2.379s 0m2.461s

Claramente se aprecia que el "system time" es similar al "cpu time". Los 2.379 segundos de "user time" slo alcanzaron para 12 millones de iteraciones. Este programa tiene evidentemente el inconveniente de obtener la hora en cada iteracin, lo que deriva en mltiples solicitudes al kernel. Esto se puede mejorar, por ejemplo, preguntando la hora cada milln de iteraciones:
#include <stdio.h> #include <sys/time.h> #include <time.h> int main(void) { int x=1, y=2, z=3; long iter1=0,iter2=0; struct timeval tv1,tv2; gettimeofday(&tv1,NULL); for(;;) { x=(x*3+y*7+z*9)%11; y=(x*9+y*11+z*3)%29; z=(x*17+y*13+z*11)%37; iter1++; if(iter1==1000000) { iter2++; iter1=0; gettimeofday(&tv2,NULL); if(tv2.tv_sec==tv1.tv_sec+5 && tv2.tv_usec>=tv1.tv_usec || tv2.tv_sec>tv1.tv_sec+5) break; } } printf("Iteraciones: %ldM Resultado: %d %d %d\n",iter2,x,y,z); return 0;

11

Mejorando la Performance en Sistemas Linux/Unix


}

Con esto, el tiempo del proceso ha sido casi exclusivamente "User Time", lo cual alcanz para 65 millones de iteraciones:
$ time ./calculo2 Iteraciones: 65M Resultado: 1 23 5 real user sys 0m5.055s 0m5.030s 0m0.001s

El nico inconveniente de esta implementacin es que se pierde cierta precisin en el tiempo de corte (se pas por 55 milsimas) debido a que no verica el tiempo con mucha regularidad. Una solucin que aprovecha ms recursos del sistema operativo se muestra a continuacin:
#include <stdio.h> #include <unistd.h> #include <signal.h> void handler(int); int timeout=0; int main(void) { int x=1, y=2, z=3; long iter1=0,iter2=0; signal(SIGALRM,handler); alarm(5); for(;;) { x=(x*3+y*7+z*9)%11; y=(x*9+y*11+z*3)%29; z=(x*17+y*13+z*11)%37; iter1++; if(iter1==1000000) { iter2++; iter1=0; } if(timeout) break; } printf("Iteraciones: %ldM Resultado: %d %d %d\n",iter2,x,y,z); return 0; } void handler(int s) { timeout=1; }

El resultado es ptimo:
$ time ./calculo3 Iteraciones: 65M Resultado: 8 17 19 real user sys 0m5.002s 0m4.977s 0m0.001s

El ltimo programa falla cuando se compila con mxima optimizacin en gcc (opcin -O3.) Esto se debe a que el optimizador descubre que la variable timeout no es cambiada en ningn lugar de main() y asume (erroneamente) que nadie ms la altera, por lo que genera un cdigo que no la verica y el loop for() se torna innito. Una forma estndar de evitar esto consiste en declarar la variable 12

Mejorando la Performance en Sistemas Linux/Unix


timeout como volatile a n de que el optimizador no haga asunciones con respecto a la misma. El cdigo de calculo3.c cambia exactamente en una lnea: $ diff calculo3.c calculo4.c 7c7 < int timeout=0; --> volatile int timeout=0;

Tras compilar con optimizacin, el resultado es an mejor: ciento tres millones de iteraciones.
$ gcc -O3 -o calculo4 calculo4.c $ time ./calculo4 Iteraciones: 103M Resultado: 2 23 3 real user sys 0m5.002s 0m4.977s 0m0.001s

A. Ejercitador de memoria
El siguiente programa permite ejercitar la memoria fsica (y el swap con los valores adecuados) con el n de aprender a observar la paginacin (con vmstat):
#include <stdio.h> #include <stdlib.h> int main(int argc,char **argv) { char *ptr; unsigned long z,k,r; unsigned long pag,off; unsigned char x; unsigned long r1,r2,r3,r4; if(argc!=3) { fprintf(stderr,"Use ram_stress <#KB> <#repeticiones>\n"); return 2; } k=atoi(argv[1]); r=atol(argv[2]); if(k<1 || r<1) { fprintf(stderr,"Se requiere valor positivo de KB y repeticiones\n"); return 1; } ptr=malloc(1024L*k); if(ptr==NULL) { fprintf(stderr,"malloc error: falta memoria?\n"); return 1; } for(z=0;z<r;z++) { r1=rand(); r2=rand(); r3=rand(); r4=rand(); pag=labs(r1*r2)%k; off=r3%1024; x=r4%256; ptr[1024*pag+off]=x; } free(ptr); return 0; }

13

Mejorando la Performance en Sistemas Linux/Unix Por ejemplo, para un sistema de 512Mb, el siguiente comando obliga a utilizar aproximadamente 50Mb para pruebas (el segundo argumento es el nmero de "pruebas" y debe ajustarse gradualmente dependiendo de la duracin deseada del test.)
$ time ./ram_stress 50000 1200000 real user sys 0m0.842s 0m0.670s 0m0.151s

En mi sistema (Athlon XP-2000) este comando tarda menos de un segundo en completarse. En cambio, si se reserva ms memoria (an manteniendo el nmero de pruebas, es decir, haciendo el mismo trabajo) el tiempo se eleva a ms de 1 minuto debido a que se ingresa al rgimen de carencia de memoria:
$ time ./ram_stress 500000 1200000 real user sys 1m9.893s 0m1.027s 0m1.896s

Ud. debera hacer estas pruebas con diferentes valores para el bloque de memoria. Observar cmo la paginacin se hace cada vez ms pronunciada conforme solicitamos un bloque que se acerca cada vez ms a la memoria fsica instalada.

Notas
1. Como todos mis textos docbook, ste ha sido escrito usando el script qdk disponible en http://qdk.sourceforge.net. 2. http://www.gnu.org/licenses/fdl.txt 3. http://www.gatogringo.com 4. Es aceptable que un proceso consuma mucho CPU por pocos segundos o minutos. Son muy escasos los procesos que consumen mucho CPU durante tiempos extendidos. 5. Para compilar el programa, usar algo como: cc -o sleep_and_run
sleep_and_run.c

6. En algunos sistemas Unix, el %CPU se toma en relacin a los ltimos segundos de ejecucin, por lo que no se presenta este inconveniente. Por otro lado, en sistemas multiprocesador el %CPU suele alcanzar 100/N% por proceso o por thread, donde N es el nmero de procesadores. 7. En algunos sistemas Unix esto se separa en columnas de "paginacin" tales como page-in y page-out. 8. De seguro al inicio entrara en el rgimen de carencia de memoria dependiendo del consumo que otros procesos hagan de esta, pero tras desplazar a aquellos al swap, el proceso pesado terminara por completo en la RAM. 9. En ocasiones, por errores de conguracin de partes de hardware, se emplean memorias muy lentas que obligan al CPU a "esperarlas", ocasionando que ste opere efectivamente a menor velocidad. 10. Para averiguar qu kernel Linux se est empleando, usar el comando "uname -r". 11. Si se conoce aproximadamente el tiempo total de los procesos que se analizan, se puede lanzar vmstat indicando este tiempo a n de obtener una nica muestra que ya no requerir de este promediado. 12. En realidad, los tiempos para este ejemplo son demasiado breves como para tomarlos como base de anlisis. Normalmente se utilizan tiempos de al menos varios minutos.

14