Práctica de Iniciación: Manejo de Temporizadores
 

Esta práctica tiene por objetivo el empleo de temporizadores que nos permitan medir correctamente los tiempos de
ejecución asociados a programas o segmentos de código.

En Unix, se dispone de temporizadores ejecutables (en concreto time) que nos proporcionan medidas de los tiempos
de ejecución de programas. Estos temporizadores nos proporcionan tres medidas de tiempo:


El tiempo real también suele recibir el nombre de elapsed time o wall time. Algunos temporizadores también proporcionan el porcentaje de tiempo que la CPU se ha dedicado al programa. Este porcentaje viene dado por la relación entre el tiempo de CPU (user + sys)
y el tiempo real, y da una idea de lo cargado que se hallaba el sistema en el momento de la ejecución del programa.

El grave inconveniente de los temporizadores ejecutables es que no son capaces de proporcionar medidas de tiempo de ejecución de segmentos de código. Para ello, hemos de invocar en nuestros propios programas  a un conjunto de temporizadores disponibles en la mayor parte de las librerías de C de Unix, que serán los que nos proporcionen medidas sobre los tiempos de ejecución de trozos discretos de código.

En nuestras prácticas vamos a emplear una función que actúe de temporizador y que nos proporcione los tiempos de CPU (user, sys)
y el tiempo real. En concreto, vamos a emplear el procedimiento uswtime listado a continuación.
 


#include <sys/resource.h>
#include <sys/time.h>

void uswtime(double *usertime, double *systime, double *walltime)
{
double mega = 1.0e-6;
struct rusage buffer;
struct timeval tp;
struct timezone tzp;

getrusage(RUSAGE_SELF, &buffer);
gettimeofday(&tp, &tzp);
*usertime = (double) buffer.ru_utime.tv_sec +
              1.0e-6 * buffer.ru_utime.tv_usec;
*systime  = (double) buffer.ru_stime.tv_sec +
              1.0e-6 * buffer.ru_stime.tv_usec;
*walltime = (double) tp.tv_sec + 1.0e-6 * tp.tv_usec;
}



Este procedimiento en realidad invoca a dos funciones de Unix: getrusage y gettimeofday. La primera de ellas nos proporciona el tiempo de CPU, tanto de usuario como de sistema, mientras que la segunda nos proporciona el tiempo real (wall time). Estas dos funciones son las que disponen de mayor resolución de todos los temporizadores disponibles en Unix.
 

Modo de Empleo:
 

La función uswtime se puede emplear para medir los tiempos de ejecución de determinados segmentos de código en nuestros programas. De forma esquemática, el empleo de esta función constaría de los siguientes pasos:

1.- Invocar a uswtime para fijar el instante a partir del cual se va a medir el tiempo.
uswtime(&utime0, &stime0, &wtime0);
2.- Ejecutar el código cuyo tiempo de ejecución se desea medir.
3.- Invocar a uswtime para establecer el instante en el cual finaliza la medición
    del tiempo de ejecución.
uswtime(&utime1, &stime1, &wtime1);
4.- Calcular los tiempos de ejecución como la diferencia entre la primera y segunda
    invocación a uswtime:

        real:   wtime1 - wtime0
        user:   utime1 - utime0
        sys :   stime1 - stime0

        El porcentaje de tiempo dedicado a la ejecución de ese segmento de código
        vendría dado por la relación CPU/Wall:

                CPU/Wall = (user + sys) / real x 100 %

El modo de empleo que se acaba de describir se puede ver claramente en el siguiente ejemplo, en el que se mide los tiempos de ejecución (de CPU usuario y sistema, y wall time) de
un cierto segmento de código:
 

main()
{
double utime0, stime0, wtime0,
       utime1, stime1, wtime1;
register int i,j,k,a;

/* ---- Primera invocación a la función. ---- */
uswtime(&utime0, &stime0, &wtime0);

/* ---- Segmento de código cuyo tiempo de ejecución se desea medir. --- */
for(i=0; i<1000; i++)
    for(j=0; j<1000; j++)
        for(k=0; k<100; k++)
             a++;

/* ---- Segunda invocación a la función. ---- */
uswtime(&utime1, &stime1, &wtime1);

/* --- Cálculo del tiempo de ejecución e impresión de resultados. --- */
printf("\n");
printf("real  %.3f\n",  wtime1 - wtime0);
printf("user  %.3f\n",  utime1 - utime0);
printf("sys   %.3f\n",  stime1 - stime0);
printf("\n");
printf("CPU/Wall   %.3f %% \n",
        100.0 * (utime1 - utime0 + stime1 - stime0) / (wtime1 - wtime0));
printf("\n");
}



 
Ejecución de la práctica:

    1.- Compile el programa anterior con el compilador gcc.
    2.- Ejecute el programa.
    3.- Ejecute de nuevo el programa invocando, además, al temporizador ejecutable "time", y compare los resultados.
            ¿Existe diferencia entre los tiempos proporcionados por el propio programa y los proporcionados por "time" ??
            ... ¿A qué cree que se debe esa diferencia?

    4.- A continuación implemente los códigos que se relacionan en la siguiente práctica: Mecanismos Básicos de
            Optimización de Código, y mida los tiempos de ejecución de los distintos segmentos de códigos mediante
            el empleo de la función uswtime, tal y como se describe en el correspondiente guión de prácticas.