Tsunami Resilience in Urban Planning and Design
The 1 st Plan The 2 nd Plan The 3 rd Plan The 4 th Plan The 5 th Plan
En el fichero polybench.c se encuentran las implementaciones de las funciones elementales necesarias para PolyBench, así como las implementaciones de las funciones prototipo defini- das en polybench.h para poder desarrollar posteriormente las distintas pruebas. Este fichero se puede ver como una librería en la que se implementan los distintos bloques funcionales de un programa de pruebas: manejo de memoria, estructuras de datos, instrumentación y acceso a APIs del sistema.
En un vistazo rápido del fichero, en seguida se llega a la conclusión de que resulta más productivo analizar a gran escala qué es lo que realiza cada una de las funciones que se imple- mentan frente a intentar explicar cada línea de código. El análisis detallado de cada función no tiene sentido ya que la mayor parte de las implementaciones son llamadas a APIs del sistema operativo y de otras librerías. Es por esto que resulta más interesante entender los problemas que intentan resolver. Para ello se enumeran las funciones existentes y se describe brevemente cuál es el objetivo de las mismas. Por simplicidad se divide este apartado en categorías lógicas y es posible que el orden de aparición de las funciones mencionadas no se corresponda con el orden de aparición en el código fuente.
Funciones de temporización.
1 static double rtclock() { ... }
• rtclock() obtiene el número de segundos y microsegundos que han pasado desde un instante prefijado en el sistema. Internamente utiliza gettimeofday() [6].
• rdtsc() lee el valor del registro TSC disponible en la arquitectura x86. Este registro al- macena el número de ciclos de la CPU desde el último reset. El valor devuelto por este registro puede no ser estable en sistemas que soporten escalado de frecuencias. Además, en sistemas multiprocesador, si el proceso migra de un procesador a otro, el valor obte- nido puede ser diferente ya que cada núcleo se puede inicializar en momentos distintos y puede escalar frecuencias de forma independiente. En principio estos problemas es- tán resueltos en arquitecturas con soporte para TSC invariante (en general, cualquier procesador Intel posterior a 2008 (aparición de los Core i) y en caso de AMD, a partir de la familia K10).
Para obtener información más detallada se puede consultar el capítulo 17.17 del manual del desarrollador de Intel [7].
Funciones de gestión de memoria para arrays creados en el montículo.
1 static void* xmalloc(size_t alloc_sz) { ... }
2 void* polybench_alloc_data(unsigned long long int n, int elt_size)
{ ... }
3 void polybench_free_data(void* ptr) { ... }
• xmalloc() es una función interna que se encarga de reservar la memoria suficiente para nuevos arrays. Esta función se encarga también del padding y el alineado de los datos en memoria.
Internamente utiliza la función posix_memalign() [8] para realizar una reserva de me- moria alineada a 4096 bytes. La cantidad de memoria reservada incluye padding y, en caso de que se haya configurado padding al principio del array, se crea una tabla en dónde se almacena el puntero original a esta memoria y se devuelve un puntero des- plazado a la dirección correspondiente al punto en dónde deben empezar los datos del array.
• polybench_alloc_data() es la función que usan los benchmarks de forma indirecta (a través de macros) para crear arrays en el montículo. Esta función llama a xmalloc() para la reserva de memoria.
• polybench_free_data() es la función que usan los benchmarks de forma indirecta (a tra- vés de macros) para liberar la memoria de arrays creados en el montículo.
Funciones de gestión de memoria para arrays con inter-padding.
3.2. Análisis de contenido
2 static void* register_padded_pointer(void* ptr, size_t orig_sz,
size_t padded_sz) { ... }
3 static void free_data_from_alloc_table (void* ptr) { ... } 4 static void check_alloc_table_state() { ... }
Este conjunto de funciones se emplea cuando los arrays tienen relleno. Lo que realizan in- ternamente es gestionar una tabla en la que se almacena un puntero al array original (con padding) y otro puntero que apunta al primer elemento del array de datos (vista de usuario). Este último puntero es con el que trabaja el programa de benchmark.
Funciones de instrumentación.
1 void polybench_prepare_instruments() { ... } 2 void polybench_timer_start() { ... }
3 void polybench_timer_stop() { ... } 4 void polybench_timer_print() { ... }
• polybench_prepare_instruments() realiza un borrado de la caché de datos y reconfigura el planificador del sistema operativo. Estas operaciones sólo las realiza si las respectivas opciones de configuración están activas.
• polybench_timer_start() llama a polybench_prepare_instruments() y almacena el instante de tiempo actual a partir de una de las funciones de temporización citadas anteriormen- te.
• polybench_timer_stop() almacena el instante de tiempo actual a partir de las funciones de temporización y restaura el planificador del sistema operativo en caso de ser nece- sario.
• polybench_timer_print() imprime por pantalla los resultados de las mediciones de tiem- po.
Funciones de instrumentación mediante PAPI.
1 static void test_fail(char *file, int line, char *call, int retval)
{ ... }
2 void polybench_papi_init() { ... } 3 void polybench_papi_close() { ... }
4 int polybench_papi_start_counter(int evid) { ... } 5 void polybench_papi_stop_counter(int evid) { ... } 6 void polybench_papi_print() { ... }
Este conjunto de funciones se encargan de gestionar el uso de los eventos PAPI que permiten conocer valores específicos de eventos hardware. La lista de eventos que se van a monitorizar se llama _polybench_papi_eventlist y se encuentra al principio del fichero, en la línea 51. En
esta lista se incluye, literalmente mediante la directiva #include de C, el contenido del fichero papi_counters.list.
Funciones del sistema.
1 void polybench_flush_cache() { ... }
2 void polybench_linux_fifo_scheduler() { ... } 3 void polybench_linux_standard_scheduler() { ... }
• polybench_flush_cache() intenta descartar la caché del procesador realizando una ope- ración lineal sobre un array grande (del orden de millones de bytes).
• polybench_linux_fifo_scheduler() cambia el planificador de tiempo de CPU para el pro- ceso actual por una implementación basada en FIFO en sistemas Linux con la prioridad máxima disponible. Requiere elevación de privilegios.
• polybench_linux_standard_scheduler() restaura el planificador de tiempo de CPU para el proceso actual. Requiere elevación de privilegios.