Lab ExecTime - Tiempo de ejecución y complejidad computacional

Hay muchos factores que pueden afectar el tiempo de ejecución de un programa, tales como el patrón de acceso a memoria, los otros programas que se estén utilizando simultáneamente, el acceso a dispositivos de entrada y salida y hasta la versión y fabricante del compilador que se utilizó para generar el ejecutable. Sin embargo la mayoría de las veces el factor dominante es la complejidad en tiempo del algoritmo implementado.

En este laboratorio implementaremos algunos algoritmos y los ejecutaremos para diferentes tamaños de input para observar el efecto que tiene el tamaño del input en el tiempo de ejecución.

Midiendo tiempo de ejecución

El siguiente código muestra una manera simple de medir el tiempo transcurrido en una función en C++.

  double elapsed_secs;
  clock_t begin, end;

  begin = clock();

  foo();           //función a la que queremos medir tiempo ejecución

  end = clock();

  elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;

La función clock() devuelve la cantidad de periodos de reloj que han transcurrido desde el comienzo del programa. Nota que en la variables begin y end estamos guardando la cantidad de períodos antes y después de la ejecución de la función foo(). Por lo tanto la resta de end - begin nos dice cuántos periodos transcurrieron durante la ejecución de foo. La constante CLOCKS_PER_SEC es el número de periodos del reloj por segundo (los GHz del procesador). Al dividir la diferencia de end y begin entre la constante CLOCKS_PER_SEC obtenemos el tiempo transcurrido.

Disclaimer: En este laboratorio mediremos tiempo de ejecución una sola vez por cada input y lo aceptamos como bueno. Sin embargo, si estuviéramos haciendo un estudio serio sobre tiempos de ejecución lo recomendable sería tomar múltiples medidas y promediar.

Todos los programas de este experimento están en https://git.ccom.uprrp.edu/rarce/ccom3034-labExecutionTime. Puedes descargarlos así:

git clone https://git.ccom.uprrp.edu/rarce/ccom3034-labExecutionTime 

Al descargarlos notarás que se creó un directorio llamado ccom3034-labExecutionTime. El directorio contiene archivos de código fuente y un archivo llamado Makefile. Para compilar todos los programas y crear sus ejecutables simplemente debes dar el comando make en la línea de comando linux.

Entregables: Escribe los resultados de las diferentes pruebas y contestaciones a preguntas en un archivo. Asegúrate de escribir tu nombre y de tu compañero al principio del archivo. Genera un PDF de ese archivo y somételo a online.uprrp.edu.

Ejercicio 1:

El programa sorting.cpp crea un vector de enteros aleatorios y luego mide los tiempos de ejecución de dos algoritmos de sorting: bubble sort (que tiene complejidad ) vs. el algoritmo de sorting que provee la librería STL (que es una mezcla de quicksort y heapsort, dos de los algoritmos de sorting O( ). El tamaño del vector debe ser especificado por el usuario al ejecutar el programa así:

./sorting <tamaño del vector>.  

Por ejemplo, ./sorting 10000, corre el programa y muestra cuántos segundos le tomó ordenar un vector de tamaño 10000 a bubble sort y al sort de STL. Corre el programa para los siguientes tamaños y observa el crecimiento de ambas implementaciones. Explica cómo tus resultados evidencian la complejidad en tiempo de los dos algoritmos.

Tamaño Tiempo para bubblesort Tiempo para STL sort
4,000
8,000
16,000
32,000

Ejercicio 2

El programa insert.cpp compara el tiempo que toma insertar elementos al principio de un vector vs. insertar al final.

El programa requiere que pases como "command line argument" el número de elementos que deseas añadir al vector. Por ejemplo, ./insert 1000 insertará 1000 elementos a un vector originalmente vacío.

Corre el programa para los siguientes tamaños y observa el crecimiento de ambas implementaciones. Según los tiempos que obtuviste, ¿la complejidad de insertar n elementos al principio es la misma que añadir n elementos al final? Usando los resultados explica cuáles crees que son las complejidades.

Número de inserciones Tiempo de insertar al principio del vector Tiempo de insertar al final del vector
40,000
80,000
160,000
320,000

Ejercicio 3

El ejercicio matrix.cpp compara el tiempo de ejecución de multiplicar una matriz por un vector vs. multiplicar una matriz por otra. Para esto crea matrices y vectores del tamaño especificado y mide el tiempo necesario para multiplicarlas.

Corre el programa para los siguientes tamaños y observa el crecimiento de ambas implementaciones. La complejidad de la multiplicación matriz-vector es de orden O() mientras que la multiplicación matriz-matriz es O(). ¿Los resultados que obtuviste para los tiempos de ejecución coinciden con esos órdenes de complejidad? Explica.

N: Tamaño de vector. Matriz es N x N. Tiempo para multiplicación matriz - vector Tiempo para multiplicación matriz - matriz
100
200
400
800

Ejercicio 4:

El programa nHighest.cpp compara dos algoritmos para encontrar los n elementos mayores de una lista:

El primer algoritmo ordena los elementos en orden ascendente usando STL sort y luego escoge los últimos 10.

El segundo algoritmo utiliza una cola de prioridades que es un estructura de datos que tiene complejidad O() para insertar / borrar

El programa requiere que pases como "command line argument" el tamaño del vector al que hallaremos los 10 elementos mayores.

Corre el programa para los siguientes tamaños y observa el crecimiento de ambas implementaciones. Usando los resultados explica cuáles crees que son las complejidades de los algoritmos.

Tamaño del vector Tiempo usando sort Tiempo usando priority queue
125,000
250,000
500,000
1,000,000

Ejercicio 5

El programa search.cpp compara dos algoritmos para encontrar un elemento dentro de una lista ordenada: binary search vs. linear search.

El programa requiere que pases como "command line argument" el tamaño del vector en el que realizaremos la búsqueda.

Corre el programa para los siguientes tamaños y observa el crecimiento de ambas implementaciones. ¿Los tiempos que obtuviste, confirman que el tiempo de binary search crece de forma logarítmica y que linear search crece de forma lineal?

Tamaño del vector Tiempo usando binary search Tiempo usando linear search
10,000
20,000
40,000
80,000

Pregunta 1: Un programa cuya complejidad es cuadrática tomó 3 segundos para un input de tamaño 1,000.

Pregunta 2: Un programa cuya complejidad es lineal tomó 30 milisegundos para un input de tamaño 1,000.