Estudio de utilización efectiva de procesadores vectoriales

Tamaño: px
Comenzar la demostración a partir de la página:

Download "Estudio de utilización efectiva de procesadores vectoriales"

Transcripción

1 Universidad de Las Palmas de Gran Canaria Estudio de utilización efectiva de procesadores vectoriales Proyecto de Fin de Carrera Ingeniería en Informática Laura Autón García Tutores: Francisca Quintana Domínguez Roger Espasa Sans Las Palmas de Gran Canaria, 9 de julio de 2014

2

3 Agradecimientos Quiero agradecer a Francisca Quintana y a Roger Espasa, mis tutores de proyecto, el haberme brindado la oportunidad de adentrarme en una experiencia que bien podría ser el sueño de cualquier futuro ingeniero informático cuando avista cada vez más cerca la meta de su esfuerzo. Este viaje no solo ha dado como resultado el presente trabajo, sino también la satisfacción profesional de haber trabajado en Intel, empresa puntera en el ámbito de la computación, y personal de haber trabajado con extraordinarios ingenieros a la vez que fantásticas personas durante todo el proceso. Entre ellos, quiero agradecer especialmente a Manel Fernández por la enorme paciencia y dedicación con las que consiguió guiarme cuando me desviaba del camino, y a Jesús Sánchez porque su buen humor y positivismo amenizaba todas las tormentas de ideas, por muy oscuras que pudieran divisarse a lo lejos. Del mismo modo, quiero agradecer muy especialmente a Susana y Delfín, por haber sido mi familia durante mi estancia en Canarias. A mis padres, María y Cándido por haber sabido apoyarme desde la distancia con sus palabras al otro lado del teléfono. Y a Raúl, mi gran compañero en este viaje, porque ha sido la única persona de este mundo que realmente ha conocido mis más profundas inquietudes, y que ha sabido iluminarme el camino y cederme las mangas sobre las que derramar mis lágrimas. i

4

5 Índice de figuras 2.1. SISD SIMD MISD MIMD Intel R Xeon Phi TM Esquema general Microarquitectura Vector Processing Unit Interconexion Directorio de etiquetas Controladores de memoria Arquitectura software de Pin Diagrama de funcionamiento CMP$im Simulación en modo buffer Simulación en modo instrucción a instrucción Ejemplo de bloque básico Proceso de descubrimiento de bloques Punteros a objetos cache Índice de vectorización de las aplicaciones de Polyhedron Razones para no vectorizar bucles en Polyhedron Índice de vectorización de las aplicaciones de Mantevo Razones para no vectorizar bucles en Mantevo Índice de vectorización de las aplicaciones de Sequoia Razones para no vectorizar bucles en Sequoia Índice de vectorización de las aplicaciones de NPB Razones para no vectorizar bucles en NPB Índice de vectorización de las aplicaciones de SPEC fp Razones para no vectorizar bucles en SPEC fp Pipeline dentro de CMP$im Pipeline del bloque de s Localización del simulador de pipeline en CMP$im Idea para la implementación de KNC Instrucción que toca dos líneas Bloques con ningún y un corte Bloques con 2 cortes Versión vectorizada vs no vectorizada de Polyhedron Ciclos desglosados de las aplicaciones de Polyhedron Versión vectorizada vs no vectorizada de Mantevo Ciclos desglosados de las aplicaciones de Mantevo Versión vectorizada vs no vectorizada de Sequoia iii

6 iv ÍNDICE DE FIGURAS 8.6. Ciclos desglosados de las aplicaciones de Sequoia Versión vectorizada vs no vectorizada de NPB Ciclos desglosados de las aplicaciones de NPB Versión vectorizada vs no vectorizada de SPEC fp Ciclos desglosados de las aplicaciones de SPEC fp Comparación entre las versiones :nodes y do de gas dyn Resultado de doblar la UL2 de 1024Kb a 2048Kb Mejora de SPEC fp/433.milc al doblar la L Consecuencia posible por aumento de aciertos en L Resultado de doblar las líneas de DTLB2 de 256 a Mejora de IS de NPB al doblar la TLB

7 Índice de tablas 4.1. Knobs soportados por Intel R ICC Desglose de instrucciones de las aplicaciones de Polyhedron Desglose de instrucciones de las aplicaciones de Mantevo Desglose de instrucciones de las aplicaciones de Sequoia Desglose de instrucciones de las aplicaciones de NPB Desglose de instrucciones de las aplicaciones de SPEC FP Latencias de memoria y de instrucción (load-op) Bloques 1 y 2 de la lista de bloques básicos más ejecutados en Fatigue, Polyhedron Bloque 3 de la lista de bloques básicos más ejecutados en Fatigue, Polyhedron Bloques 1, 2, 4 y 5 de la lista de bloques básicos más ejecutados en Induct, Polyhedron Bloques 1 y 2 más ejecutados de Aermod, Polyhedron Bloques 3, 5 y 10 más ejecutados de Aermod, Polyhedron Bloques 7 y 9 más ejecutados de Aermod, Polyhedron Bloque 8 más ejecutado de Aermod, Polyhedron Desglose de instrucciones de las versiones escalar y vectorial de Gas dyn, Polyhedron Bloques 1 y 3 más ejecutados de Gas dyn, Polyhedron Bloques 1, 2, 3 y 4 más ejecutados de SPhotmk, Sequoia Bloque 1 de los más ejecutados de BT, NPB Bloques 1 y 2 de los más ejecutados de LU, NPB Bloques 1 y 2 de los más ejecutados de Povray, SPEC FP Aplicaciones con una mejora inferior al 1 % A.1. Intel R ICC Specific Pragmas B.1. Intel R ICC Supported Pragmas C.1. Intel R Fotran Directives D.1. Mensajes del compilador v

8

9 Índice general Agradecimientos Lista de figuras Lista de tablas I VIII VIII 1. Introducción Objetivos Estado del arte Taxonomía de Flynn Vectorización SIMD Intel R Xeon Phi TM Coprocessor Microarquitectura Intel R Advanced Vector Extensions Intel R Advanced Vector Extensions Intel R Advanced Vector Extensions Intel R Advanced Vector Extensions Metodología Plan de trabajo Herramientas Pin Pintools Arquitectura software CMP$im Benchmarks Polyhedron Fortran Benchmarks Mantevo ASC Sequoia Benchmark Codes NAS Parallel Benchmarks SPEC CPU Compiladores ICC IFORT Pragmas Herramientas internas Arquitectura del Simulador Flujo de ejecución Estructuras y clases Parámetros de ejecución vii

10 viii ÍNDICE GENERAL 6. Caracterización de benchmarks Polyhedron Mantevo Sequoia NPB SPEC FP Adaptación del Simulador Pipeline Detección de instrucciones y registros Instrucciones Registros Latencias Nuevas estructuras y clases Estadísticas Invocación activando la funcionalidad vectorial Estudio experimental Resultados Polyhedron Mantevo Sequoia NPB SPEC fp Diagnóstico Software Polyhedron Mantevo Sequoia NPB SPEC fp Diagnóstico Hardware Incremento de UL Incremento de TLB Conclusiones Trabajo Futuro A. Intel R ICC Specific Pragmas 105 B. Intel R ICC Supported Pragmas 109 C. Intel R Fortran Directives 113 D. Mensajes del compilador 117

11 Capítulo 1 Introducción Echando una profunda mirada al pasado para recorrer toda la historia de la informática, desde donde venimos, en qué punto nos encontramos y a dónde vamos, nos damos cuenta del gran esfuerzo que ha hecho y sigue haciendo el ser humano para no solo automatizar tareas, sino también para que éstas se hagan lo más rápido posible. Y es que, ya en su momento, para realizar cálculos balísticos de gran utilidad en posibles contiendas, se incrementaba el número de personas que realizaban una tarea. Al segmentar el trabajo se conseguía realizar la tarea en menos tiempo de lo que lo conseguiría una sola. A estas personas se las denominaba antiguamente computers[bar08], nombre que más tarde se adoptó para las máquinas que sustituyeron su trabajo. Desde entonces, la capacidad de cómputo de las máquinas ha ido evolucionado enormemente gracias a, por ejemplo, la cantidad de transistores que fuimos capaces de insertar dentro de una onza de silicio y que bien supo pronosticar Gordon Moore, cuya afirmación se bautizo como Ley de Moore. Cuando las limitaciones físicas se convirtieron en un problema, empezamos a introducir más núcleos en un mismo procesador: primero dos, luego cuatro... Está claro que, sean los motivos que sean los que impulsen al ser humano a seguir escudriñando mejoras en cualquier tipo de artefacto, mecanismo o sistema que tenga entre manos, y sobre el que haya trabajado desde tiempos inmemoriales, el mundo, inexorablemente, se sigue moviendo. Y es en ese mundo en constante cambio y movimiento, donde acaban por surgir ideas como aquella sobre la que se ha construido el trabajo que se presenta: la vectorización. Hoy en día, una importante muestra de procesadores disponibles en el mercado disponen de unidades de cómputo, denominadas vectoriales, que permiten la explotación de este concepto. Y es que la vectorización explota un caso particular de paralelismo cuyo objetivo consiste en realizar la misma operación, en vez de sobre un único dato como venía siendo hasta ahora, sobre la mayor cantidad de datos contenidos en un vector que le sea posible. Por ello, se denomina DLP (Data Level Parallelism) o Paralelismo de datos. Algunos de estos procesadores, por ejemplo, son los de Intel R basados en la arquitectura Sandy Bridge que, con el objetivo de permitir la explotación del paralelismo de datos, incluyen extensiones AVX (Advanced Vector Extensions) sobre el repertorio de instrucciones x86. Sin embargo, esta obra de ingeniería no es suficiente por sí sola. Es necesario un engranaje más, y que no es otro que un compilador especialmente construido para máquinas como estas, que sea capaz de extraer el mayor paralelismo de datos posible de una aplicación. Pese a que todos los elementos mencionados conforman la receta perfecta para sacar el mayor rendimiento posible a aplicaciones que requieren de una importante capacidad de cómputo, no siempre se obtienen los resultados esperados. Las razones pueden residir tanto en el software como en el hardware. Puede que la aplicación no experimente las mejoras esperadas después de ser vectorizada. Es posible que el compilador no sea capaz por sí mismo de encontrar potenciales secciones de código vectorizables debido a ambigüedades en el acceso a los datos. O bien, podría 1

12 2 CAPÍTULO 1. INTRODUCCIÓN ser que la memoria esté suponiendo un cuello de botella a la hora de recuperar los datos sobre los que operar. Basándonos pues en la realidad descrita, se propuso la realización del trabajo que se detalla en este documento, con el objetivo fundamental de determinar el grado de utilización efectiva de la unidad vectorial de un procesador. Se realizaría entonces, para aquellos casos donde el uso fuera menor del esperado, un diagnóstico del problema que permitiera lograr una mejora en el rendimiento de la aplicación Objetivos El objetivo principal de este Proyecto Final de Carrera, consiste en determinar el grado de utilización efectiva de la unidad vectorial de un procesador. Para lograr la consecución del mismo, se proponen los siguientes objetivos parciales: Analizar y clasificar un conjunto de aplicaciones numéricas en función del grado de vectorización sobre un compilador determinado. Determinar las causas del bajo grado de vectorización, a partir de la simulación de las aplicaciones según el funcionamiento de un producto existente que hace uso de la unidad vectorial. Las posibles causas serán las siguientes: Problemática en el algoritmo base de la aplicación debido a dependencias en el código. Problemática en los criterios seguidos a la hora de escribir el código fuente. Incapacidad del compilador de detectar que el código es vectorizable. Problemas en la microarquitectura. Proponer cambios hardware/software que faciliten el uso efectivo de la unidad vectorial.

13 Capítulo 2 Estado del arte La computación paralela es una forma de cómputo consistente en paralelizar la mayor cantidad de tareas posible con el objetivo de reducir el coste de cómputo de un programa. Tradicionalmente se utilizaba otro paradigma: la computación serie. Con ella las instrucciones se ejecutaban una tras otra en la Unidad Central de Procesamiento (CPU). La utilización de este paradigma produce que, a medida que se incrementa la frecuencia de funcionamiento de la máquina, se disminiuya el tiempo que tardan en ejecutarse los programas[hp02]. El aumento de la frecuencia, que tuvo su apogeo durante las dos últimas décadas del siglo XX y principios del XXI, no podía ser infinito, ya que es directamente proporcional al aumento de la energía consumida por el procesador y, por ende, a la generación de calor. Por este motivo, pese a que la computación paralela se empezó a usar principalmente en el área de la computación de altas prestaciones, este límite en el aumento de la frecuencia propició que desde la ultima década, el paradigma principal en arquitectura de computadores sea la computación paralela.[bar07] Existen diferentes fuentes de paralelismo disponibles para sacar partido a la computación paralela. Estas son: Paralelismo de Instrucciones (ILP), Paralelismo de Datos (DLP) y Paralelismo de Tareas (TLP)[Dí06]: ILP: consiste en ejecutar el mayor número de instrucciones posibles en paralelo sin que ello afecte al correcto flujo del programa. Como ejemplos tenemos las arquitecturas superescalares y VLIW, del inglés Very Long Instruction Word: Superescalares: capaces de introducir en el pipeline de ejecución una o más instrucciones por ciclo, de manera que se pueden estar ejecutando paralelamente varias en un mismo ciclo. VLIW: la arquitectura permite empaquetar varias instrucciones independientes que se ejecutarán simultáneamente. Con el objetivo de explotar al máximo esta fuente de paralelismo, existen diferentes técnicas que se podrían clasificar en técnicas de planificación estática y dinámica[dí06]. Técnicas de planificación estática: trabajan sobre el código de la aplicación para conseguir eliminar todos los obstáculos que impiden que las instrucciones se ejecuten lo antes posible: desenrollamiento de bucles, reordenamiento de instrucciones... Técnicas de planificación dinámica: se aplican sobre el diseño del hardware para que tengan lugar en tiempo de ejecución: Ejecución Fuera de Orden (OOO). DLP: consiste en la realización de la misma operación simultáneamente sobre un conjunto de datos. Para explotar esta técnica de paralelismo, es necesario que el programa tenga 3

14 4 CAPÍTULO 2. ESTADO DEL ARTE secciones de código que se puedan adaptar a la aplicación de este concepto. Además, la arquitectura tiene que proveer de instrucciones especiales, denominadas SIMD, del ingles Simple Instruction Multiple Data, y de recursos suficientes, como por ejemplo los registros vectoriales, los cuales puedan contener más de un dato. La estructura de ejecución por antonomasia sobre la que se pone en práctica este mecanismo, es el bucle. Las estructuras de datos análogas son los vectores y matrices. TLP: el concepto radica en la descomposición de la ejecución del programa en diferentes trazas con instrucciones independientes para ejecutarlas de forma concurrente. Un ejemplo es la Tecnología Multihilos, SMT, del inglés Simultaneous MultiThreading. Consiste en ejecutar instrucciones de diferentes hilos independientes en el mismo ciclo de reloj. En el mercado existen una gran variedad de arquitecturas que implementan recursos para explotar cualquiera de las fuentes de paralelismo arriba mencionadas. Por ejemplo, la arquitectura ARM con su extensión SIMD Avanzada, también conocida como NEON o MPE, del inglés Media Processing Engine, para explotar el paralelismo de instrucciones; Nvidia y su plataforma CUDA, del inglés Computed Unified Device Architecture, junto con el set de instrucciones PTX, del inglés Parallel Thread Execution[nvi], que define una máquina virtual y un ISA con el objetivo de explotar la GPU como máquina de ejecución de hilos paralelos de propósito general; Intel R y su arquitectura Intel R MIC, del inglés Many Integrated Core, sobre la que han desarrollado varios productos, siendo el último de ellos el Intel R Xeon Phi TM Coprocessor, lanzado al mercado en Noviembre de 2012 y descrito en la Sección 2.3 (pág. 8), que explota tanto el paralelismo de instrucciones como de tareas Taxonomía de Flynn La taxonomía de Flynn consiste en una clasificación de arquitecturas paralelas desarrollada por Michael J. Flynn en 1966 y expandida en 1972[Fly72]. Desde el punto de vista del programador en lenguaje ensamblador, las arquitecturas paralelas estarían clasificadas según la concurrencia del procesamiento de secuencias, datos e instrucciones. Esto da como resultado una metodología de clasificación de las distintas operaciones paralelas disponibles en el procesador. Propuesta como una aproximación que clarificara los tipos de paralelismo soportados tanto a nivel hardware como software, en ella se definen las siguientes cuatro arquitecturas[fly]: Single Instruction Single Data (SISD): arquitectura secuencial que no explota el paralelismo ni a nivel de instrucciones ni a nivel de datos. Las máquinas tradicionales de un único procesador secuencial o antiguos mainframes entrarían en esta categorización. Ver Figura 2.1 (pág. 5). Single Instruction Multiple Data (SIMD): arquitectura que explota el paralelismo durante la ejecución de una única instrucción para realizar operaciones de naturaleza paralela. Claros ejemplos son los procesadores vectoriales o las GPU. Ver Figura 2.2 (pág. 5). Multiple instruction Single Data (MISD): múltiples instrucciones operan sobre un único stream de datos. Es una arquitectura poco común generalmente usada para tolerancia de fallos, esto es, varios sistemas operan en el mismo stream de datos y obtienen un resultado que debe ser concorde para todos ellos. Ver Figura 2.3 (pág. 5). Multiple instruction Multiple Data (MIMD): múltiples procesadores executan simultáneamente diferentes instrucciones sobre diferentes datos. Las arquitecturas VLIW son un claro ejemplo aparte de sistemas distribuidos o procesadores multicore. Ver Figura 2.4 (pág. 5).

15 2.2. VECTORIZACIÓN 5 Figura 2.1: SISD Figura 2.2: SIMD Figura 2.3: MISD Figura 2.4: MIMD 2.2. Vectorización La vectorización es un proceso de explotación de paralelismo de datos consistente en convertir un algoritmo de implementación escalar a vectorial. La implementación escalar es aquella en la que se realiza una única operación simultánea sobre un par de operandos que contienen un único dato cada uno. La implementación vectorial realizaría la misma operación, pero el par de operandos pasan de contener un único dato a contener una serie de valores. Literalmente, las escalares operan sobre un escalar y las vectoriales sobre un vector. El bucle siguiente es un claro ejemplo de candidato a ser vectorizado. 1 for ( i =0; i<n; i ++) 2 c[ i] = a[ i] + b[ i]; Listado 2.1: Bucle vectorizable Con el objetivo de poner en práctica esta técnica, es necesario que la arquitectura sobre la que se va a ejecutar el programa disponga de un repertorio de instrucciones específico, de una unidad vectorial y de registros vectoriales que puedan contener una serie de datos[lpg13] [SMP11] [Pip12] [SLA05]. Este repertorio de instrucciones suele ser una extensión sobre las que ya conformen la ISA con la que se trabaje, denominadas instrucciones SIMD. SIMD también sirve para indicar el tipo de la máquina. La unidad vectorial contendrá las unidades funcionales necesarias para operar sobre los registros vectoriales. Finalmente, para generar el código vectorizado, hace falta utilizar un compilador que sea capaz de reconocer secciones de código potencialmente vectoriales para generar las instrucciones SIMD que correspondan SIMD SIMD, como se describió previamente, es un término definido dentro de la taxonomía de Flynn para caracterizar aquellas arquitecturas que explotan el Paralelismo de Datos (DLP). Son arquitecturas que contienen múltiples elementos de procesamiento que permiten realizar la misma operación sobre varios datos simultáneamente. Esta forma de paralelismo se diferencia de la concurrencia en que es en el mismo momento, y no en momentos diferentes, cuando se realiza de forma simultánea la misma operación sobre el conjunto de datos que corresponda. Estas instrucciones se denominan comúnmente como instrucciones SIMD. Un procesador vectorial, por tanto, es un procesador que puede operar en todos los elementos de un vector dado con una única instrucción. Esto supone una reducción importante en el número de instrucciones leídas, decodificadas y ejecutadas para un mismo programa vectorizado, frente a otro que no haya sido compilado o escrito usando este recurso. El vector sobre el que se opere, puede a su vez ser leído usando diferentes técnicas que diferencian a unos procesadores vectoriales de otros. En el caso de ser una arquitectura vectorial memoria-memoria, los operandos son inyectados a la

16 6 CAPÍTULO 2. ESTADO DEL ARTE unidad vectorial directamente de memoria, siendo el resultado devuelto a la misma a posteriori. En el caso de las arquitecturas vectoriales vector-registro, los operandos son depositados en registros vectoriales que alimentaran a la unidad vectorial, siendo asimismo el resultado depositado en otro registro vectorial[ms03]. Independientemente de sendos modus operandi descritos, la mejora en la reducción del número de instrucciones leídas y decodificadas resulta no ser tal si al final la instrucción va a tener que esperar a que se lea el vector o porción del vector de memoria. Por esto, existen diversos esquemas de optimización del rendimiento centrados en reducir el tiempo de acceso a la memoria: Hardware y software prefetching: prefetching en términos generales significa traer datos o instrucciones de memoria antes de que se necesiten. Cuando la aplicación necesita datos que se han traído con el prefetching, puede tomarlos directamente en vez de tener que esperar por la memoria. Esta técnica puede ser iniciada tanto desde el hardware como desde el software. Gather-scatter: estas instrucciones permiten un tipo de direccionamiento de memoria propio del tratamiento de vectores. El gather se encargaría de indexar la lectura del vector mientras que el scatter se encargaría de la escritura. En su funcionamiento intervienen máscaras que indicarían los elementos del vector sobre los que se realizaría la operación. Estas podrían ser útiles en caso de haber condiciones en el interior del bucle para acceder a datos de forma dispersa. Stripmining: técnica que afronta un problema en la vectorización consistente en que los registros vectoriales no tienen por qué ser capaces de contener un vector completo definido en la aplicación. Consistiría en romper el bucle de la aplicación que opera sobre el vector en diferentes bucles: prólogo, principal y epílogo según conviniera, para tratar un número de datos <= MV L, donde MVL es la Longitud Máxima de Vector del procesador. Bancos de memoria: permiten realizar varios load y stores simultáneamente. Los datos tratados cada vez no tendrían por qué ser necesariamente secuenciales. Repertorio de instrucciones SIMD El primer uso de instrucciones SIMD fue en los supercomputadores vectoriales a principios de los 70. Sin embargo, el modo de funcionamiento de entonces era ligeramente distinto al concepto actual. Anteriormente la instrucción que operaba sobre el vector lo hacía sobre un pipeline de procesadores, cada uno de los cuales operaba únicamente sobre una palabra del vector. En el concepto moderno, es un procesador el que realiza la operación simultáneamente sobre el vector completo o una porción del mismo. A lo largo del tiempo, esta tecnología salto a las máquinas sobremesa, mercado en el que tuvo gran repercusión y posterior demanda debido a que podía soportar aplicaciones tales como procesamiento de vídeo y juegos en tiempo real. Es por ello que a lo largo de la historia ha habido gran variedad de repertorios SIMD fruto de la competencia entre diferentes compañías del momento. Hoy en día no hay computadora de uso general que no haga uso de una arquitectura que explote el paralelismo de datos a través de la vectorización. A continuación se nombran algunos de los diferentes repertorios de instrucciones SIMD de la historia: VIS: Visual Instruction Set. Repertorio desarrollado y lanzado al mercado en 1994 por Sun Microsystems, adquirida por Oracle Corporation en La primera version fue implementada en el UltraSPARC en 1995 y por Fujitsu en el SPARC64 GP. Registros de 8, 16 y 32 bits. Hubieron varias versiones posteriores esta: VIS2, VIS2+ y VIS3. MMX: repertorio desarrollado por Intel R y lanzado en 1997 en su gama Pentium MMX. Registros de 64 bits.

17 2.2. VECTORIZACIÓN 7 3DNow!: extensión al repertorio de la arquitectura x86 desarrollado por AMD. Registros 32 bits para operaciones de punto flotante de simple precisión. El objetivo era mejorar el ya existente repertorio MMX de Intel de cara a elevar el rendimiento de las aplicaciones gráficas. En 2010 AMD anunció el fin del mantenimiento y soporte del mismo. SSE: Streaming SIMD Extensions. Extensión de la arquitectura x86 diseñado por Intel y lanzado al mercado en 1999 en su serie Pentium III, como respuesta al lanzamiento por parte de AMD de su extensión 3DNow. Registros de 128 bits. AltiVec: repertorio diseñado por y propiedad de la siguientes empreas: Apple, aquí recibe el nombre de Velocity Engine; IBM, donde se denomina VMX; Freescale Semiconductor, propietaria de la marca registrada AltiVec. Registros de 128 bits. AVX: Advanced Vector Extensions. Extensión al repertorio de la arquitectura x86 destinada a procesadores Intel y AMD. Registros de 256 bits. Su desarrollo fue propuesto por Intel en 2008 pero no fue hasta tres años después cuando salió al mercado como característica de la generación de procesadores Sandy Bridge de Intel y Bulldozer de AMD. Posterior a ella tenemos otra extension denominada AVX2 soportada por Haswell, Broadwell, Skylake y Cannonlake de Intel y por Excavator de AMD. En Julio de 2013 Intel anunció AVX-512, última extensión con registros de 512 bits. Ventajas Los procesadores vectoriales y por extensión el uso de la vectorización, proporcionan las siguientes ventajas: Los programas son de menor tamaño al reducir el número de instrucciones que pudiera requerir un bucle. Ademas, el número de instrucciones ejecutadas también se ve reducido al poder concentrar un bucle en una única instrucción. El rendimiento de la aplicación mejora. Se tienen N operaciones independientes que utilizan la misma unidad funcional, explotando al máximo la localidad espacial de la memoria cache. Las arquitecturas son escalables: se obtiene un mayor rendimiento cuantos más recursos hardware haya disponibles. El consumo de energía se reduce. Mientras la instrucción vectorial se está ejecutando, no es necesario alimentar otras unidades tales como el ROB o el decodificador de instrucciones. Tomando como ejemplo una aplicación multimedia que cambia el brillo a una imagen, la vectorización proporciona dos mejoras claras: en primer lugar el conjunto de datos se entiende como un vector y no como valores individuales. Esto permitirá cargar en un registro todos aquellos datos que éste pueda albergar, en vez de convertir el programa en una retahíla carga-opera-guarda sobre una gran cantidad de píxeles individuales. En segundo lugar, la paralelización del trabajo en una única instrucción es evidente. Cuanto más datos pueda albergar, mayor será el rendimiento. Inconvenientes Los procesadores vectoriales comparados con multiprocesadores o procesadores superescalares podrían resultar menos interesantes si lo miramos desde el punto de vista del coste: Necesitan memoria on-chip rápida y por consiguiente cara.

18 8 CAPÍTULO 2. ESTADO DEL ARTE Hay que diseñarlos de forma específica. La unidad vectorial de los procesadores vectoriales no se compone de elementos prefabricados más allá de las unidades básicas, por tanto pocas ventas del producto final se traduciría en pérdidas debido a su coste de diseño y validación. Mientras que se conseguía una ventaja en el consumo de energía al reducir la alimentación de otras unidades, el consumo por parte de los registros vectoriales podría no mejorar el balance final de consumo. Los compiladores, al igual que pueden facilitar la tarea, pueden dificultarla en caso de que la vectorización del código no se adapte a los requerimientos esperados por el compilador. Existe entonces una posibilidad real de que haya una importante implicación del ingeniero tanto a nivel alto como bajo para conseguir vectorizar una aplicación. No hay un estándar establecido en el proceso. La utilización de un repertorio de instrucciones SIMD u otro puede dificultar la tarea en caso de compilar la misma aplicación sobre distintas arquitecturas. Aparte, es posible que el ingeniero tenga que proveer de una implementación no vectorial de la misma. No todas las aplicaciones se pueden vectorizar. Por ejemplo, las aplicaciones de análisis de código, caracterizadas por su fuerte control del flujo de ejecución, son claras candidatas para no beneficiarse de las ventajas de la vectorización Intel R Xeon Phi TM Coprocessor El coprocesador Intel R Xeon Phi TM es el primer producto basado en la arquitectura Intel R MIC que se ha comercializado. Se lanzó al mercado en Noviembre de La arquitectura Intel R MIC se basa en la combinación de muchos núcleos de Intel R dentro de un único chip. Esta destinada a su uso en la Computación de Alto Rendimiento o HPC, del inglés High Performance Computing, para la ejecución de programas paralelos que se venían ejecutando en grandes clústeres 1. Pese a que su objetivo no es sustituir los sistemas ya existentes, es una interesante alternativa para conseguir buenos resultados de rendimiento de throughput 2, en entornos donde no haya demasiado espacio para la instalación de múltiples clústeres y donde se impongan limitaciones de consumo de energía. Ademas, un punto clave de la microarquitectura es que esta construida especialmente para proporcionar un entorno de programación similar al entorno de programación del procesador Intel R Xeon TM [intb]. El coprocesador Intel R Xeon Phi TM puede pasar por un sistema en sí mismo, puesto que corre una distribución completa del sistema operativo Linux, soporta el modelo x86 de ordenamiento de memoria y el estándar IEEE 754 de aritmética en punto flotante. Ademas es capaz de ejecutar aplicaciones escritas en lenguajes de programación propios de la industria del HPC como es el caso de Fortran, C y C++. Esto permite proporcionar con el producto un rico entorno de desarrollo que incluye compiladores, numerosas librerías de apoyo (siendo de especial importancia aquellas con soporte multi-thread y operaciones matemáticas para HPC) y herramientas de caracterización y depurado. Está conectado a un procesador Intel R Xeon, denominado host, a través de un bus PIC Express. Véase Figura 2.6 (pág. 9). Dado que el coprocesador ejecuta de forma autónoma el sistema operativo Linux, es posible virtualizar una comunicación tcp/ip entre éste y el procesador, permitiendo al usuario acceder como si fuera un nodo más en la red. Por tanto, cualquier usuario puede conectarse al mismo a través de una sesión ssh (secure shell) y ejecutar sus aplicaciones. Además, soporta aplicaciones heterogéneas en las que una parte de la misma se ejecutaría en el host y otra en la propia tarjeta. Ni qué decir tiene que se pueden conectar más de un Xeon 1 Se aplica a los conjuntos de computadoras construidos mediante la utilización de elementos hardware comunes y que se comportan como si fuesen una única computadora 2 Cantidad de trabajo que un ordenador puede hacer en un periodo de tiempo determinado

19 2.3. INTEL R XEON PHI TM COPROCESSOR 9 Figura 2.5: Intel R Xeon Phi TM Phi en un mismo sistema, pudiéndose establecer entre ellos la comunicación ya sea a través de la interconexión p2p (peer to peer) o a través de la tarjeta de red del sistema, sin intervención en ambos casos del host. Figura 2.6: Esquema general Microarquitectura El coprocesador Intel R Xeon Phi esta formado por más de 50 núcleos de procesamiento, memorias cache, controladores de memoria, lógica de cliente PCIe y un anillo de interconexión bidireccional que proporciona un elevado ancho de banda al sistema. Véase Figura 2.7 (pág. 10). La ejecución es en orden mientras que la terminación es en desorden. Cada core consta de una L2 privada que mantiene completamente la coherencia con el resto gracias a un directorio de etiquetas distribuido denominado TD, del inglés Tag Directory. Los controladores de memoria y la lógica de cliente PCIe proporcionan una interfaz directa con la memoria GDDR5 del coprocesador y el bus PCIe respectivamente. Ademas, cada core fue diseñado para minimizar el uso de energía a la vez que maximiza el throughput en programas altamente paralelos. Usan un pipeline en orden y soportan hasta 4 hilos hardware.

20 10 CAPÍTULO 2. ESTADO DEL ARTE Figura 2.7: Microarquitectura VPU Un importante componente de cada núcleo del coprocesador Intel R Xeon Phi TM es la VPU. Véase Figura 2.8 (pág. 10). La VPU cuenta con un repertorio de instrucciones SIMD de 512 bits, oficialmente conocido como Intel R Initial Many Core Instructions (Intel R IMCI). Por ello puede ejecutar 16 operaciones de simple precisión (SP) u 8 de doble precisión (DP) por ciclo. También soporta instrucciones Fused Multiply-Add (FMA), que ordenan multiplicar y sumar en la misma instrucción, y gracias a las cuales se pueden ejecutar 32 instrucciones de simple precisión o 16 de punto flotante por ciclo. Ni qué decir tiene que proporciona soporte para operaciones con enteros. Figura 2.8: Vector Processing Unit Las unidades vectoriales proporcionan una evidente mejora energética en la ejecución de aplicaciones HPC, ya que una única operación codifica una gran cantidad de trabajo, a la vez que no incurre en el coste adicional de energía que supondrían las etapas de fetch, decode y retire para la ejecución de múltiples instrucciones. Sin embargo, hicieron falta varias mejoras para lograr soportar instrucciones SIMD de estas características. Por ejemplo, se añadió el uso de máscaras a la VPU para permitir predecir sobre qué datos operar dentro de un registro vectorial. Esto ayudó en la vectorización de bucles con flujos de ejecución condicionales, mejorando así la eficiencia software del pipeline. La VPU tambien soporta instrucciones de tipo gather y scatter directamente a través del hardware. De este modo, para aquellos códigos con patrones de acceso a memoria esporádicos e irregulares, el uso de este tipo de instrucciones ayuda a mantener el código vectorizado. Finalmente, la VPU también cuenta con una EMU, del inglés Extended Math Unit, que puede ejecutar instrucciones trascendentes como son las recíprocas, raíces cuadradas y logarítmicas. La EMU funciona calculando aproximaciones polinómicas de estas funciones.

21 2.3. INTEL R XEON PHI TM COPROCESSOR 11 Interconexión La interconexión se implementa como un anillo bidireccional. Véase Figura 2.9 (pág. 11). Cada dirección está compuesta de tres anillos independientes. El primero, que se corresponde con el más ancho y caro de los tres, es el anillo de datos. Este es de 64 bytes para soportar el requisito de gran ancho de banda debido a la gran cantidad de cores presentes. El anillo de direcciones es más estrecho y se utiliza para enviar comandos de lectura/escritura y direcciones de memoria. Por último, el anillo más estrecho y barato es el anillo de reconocimiento, que envía mensajes de control de flujo y coherencia. Figura 2.9: Interconexion Figura 2.10: Directorio de etiquetas Cuando un core accede a su cache L2 y falla, una solicitud de dirección se envía sobre el anillo de direcciones a los directorios de etiquetas. Véase Figura 2.10 (pág. 11). Las direcciones de memoria se distribuyen de manera uniforme entre los distintos directorios que hay en el anillo para añadir la fluidez de tráfico como una característica más del mismo. Si el bloque de datos solicitado se encuentra en la cache L2 de otro core, se dirige una petición a la L2 de ese core sobre el anillo de direcciones. Finalmente, el bloque de solicitud es posteriormente reenviado sobre el anillo de datos. Si los datos solicitados no se encuentran en ninguna de las caches, se envía la dirección de memoria desde el directorio de etiqueta hasta el controlador de memoria. La Figura 2.11 (pág. 12) muestra la distribución de los controladores de memoria en el anillo. Como se aprecia, se intercalan de forma simétrica alrededor del él. La asignación de los directorios de etiquetas a los controladores de memoria se realiza de forma todos-a-todos. Las direcciones se distribuyen uniformemente a través de todos los controladores, eliminando de este modo los hotspots y proporcionando un patrón de acceso uniforme esencial para un uso efectivo del ancho de banda. Volviendo al modo de funcionamiento, durante un acceso de memoria, cada vez que se produce un error en el nivel L2 de cache en un core, éste genera una petición de dirección en el anillo

22 12 CAPÍTULO 2. ESTADO DEL ARTE Figura 2.11: Controladores de memoria de direcciones y consulta a los directorios de etiquetas. Si los datos no se encuentran en estos directorios, el core genera otra solicitud de dirección y solicita los datos a la memoria. Una vez que el controlador recibe el bloque de datos desde la memoria, se entrega al core a través del anillo de datos. En todo el proceso los elementos trasmitidos a los anillos son: un bloque de datos, dos solicitudes de dirección junto con dos mensajes de confirmación. Debido a que los anillos de datos son los más caros y están diseñados para soportar el ancho de banda requerido, es necesario incrementar el número de anillos de dirección y reconocimiento, más baratos en comparación, en un factor de dos para soportar las necesidades de ancho de banda causadas por el elevado número de peticiones sobre los anillos. Caches La arquitectura Intel R MIC invierte en mayor medida tanto en caches L1 como L2 en comparación con las arquitecturas GPU. El coprocesador Intel R Xeon Phi TM implementa un subsistema de memoria en el que cada core está equipado con una cache de instrucciones L1 de 32KB, una cache de datos L1 de 32KB y una cache L2 unificada de 512KB. Son totalmente coherentes e implementan el modelo de orden de memoria x86. Las caches L1 y L2 proporcionan un ancho de banda agregado que es entre 15 y 7 veces, respectivamente, más rápido que el ancho de banda de la memoria principal. Por lo tanto, el uso efectivo de esta jerarquía es clave para lograr el máximo rendimiento en el coprocesador. Además de mejorar el ancho de banda, son también más eficientes que la memoria principal en cuanto al uso de energía para el suministro de datos al core. En la era de la computación exascale 3, las caches jugarán un papel crucial a la hora de conseguir maximizar el rendimiento bajo estrictas restricciones de potencia. Imágenes cortesía de Intel R. 3 La computacion exascale se refiere a los sistemas de computacion capaces de alcanzar un exaflops.

23 2.4. INTEL R ADVANCED VECTOR EXTENSIONS Intel R Advanced Vector Extensions AVX, del inglés Advanced Vector Extensions engloba, como veíamos en la Sección (pág. 6), el conjunto de extensiones sobre la arquitectura del repertorio de instrucciones x86, propuestas por primera vez por Intel en Marzo de 2008 tanto para procesadores de Intel como de AMD. El primer producto en soportarlo fue el procesador Sandy Bridge de Intel en el primer cuarto de 2011, seguido por el procesador Bulldozer de AMD en el tercer cuarto del mismo año Intel R Advanced Vector Extensions 1 Las extensiones Intel R Advanced Vector Extensions 1 (AVX) mejoraban las extensiones SSE mediante el incremento del ancho del banco de registros SIMD de 128 bits a 256 bits. El nombre de los registros, XMM0-XMM7, se cambió en consecuencia de YMM0-YMM7 (en el caso de x86-64, YMM0-YMM15). Sin embargo, en los procesadores con soporte AVX, las instrucciones de la extensión SSE podían ser usadas para operar en los 128 bits menos significativos de los registros YMM. Entonces podía seguir usándose la nomenclatura XMM0-XMM7. AVX introdujo además un formato de instrucción SIMD de tres operandos donde el registro de destino podía ser distinto a los dos registros fuente. Por ejemplo, una instrucción SSE usando la forma convencional a = a + b, podía ahora utilizar el método de tres operandos c = a + b, impidiendo que se destruyera la información almacenada en alguno de ellos como ocurría hasta el momento. Este formato estaba limitado a las instrucciones que utilizan los registros YMM, no incluyendo por tanto instrucciones con registros de propósito general (por ejemplo EAX) Intel R Advanced Vector Extensions 2 Las extensiones Intel R Advanced Vector Extensions 2 (AVX2) mejoraban el set de extensiones AVX, y fueron introducidas por primera vez en la microarquitectura Intel R Haswell. La compañía amplió por tanto el juego AVX con nuevas instrucciones que funcionaban también sobre números naturales, ampliando casi la totalidad del conjunto SSE de 128 bits a 256 bits. El formato no destructivo de tres operandos estuvo ahora también disponible para instrucciones a nivel de bits y multiplicación de propósito general y para instrucciones FMA (Fused Multiply-Accumulate). Finalmente, esta nueva ampliación permitió realizar instrucciones gather, lo que significaría la posibilidad de acceder a la vez a varias posiciones no contiguas en memoria, aumentando considerablemente las capacidades de procesado vectorial de la arquitectura x Intel R Advanced Vector Extensions 512 Intel R Advanced Vector Extensions 512, AVX-512, son las extensiones a 512 bits de las instrucciones SIMD recogidas en las Advanced Vector Extensions de 256 bits. Fueron propuestas por Intel en Julio de 2013 para ser incluidas en el coprocesador Intel R Xeon Phi TM denominado Knights Landing que se espera lanzar al mercado en el año 2015[inta]. No todas las extensiones están destinadas a ser soportadas por todos los procesadores que las implementen. Sólo la extensión del núcleo AVX-512F (AVX-512 Foundation) se requiere para todas las implementaciones. Atendiendo al repertorio de instrucciones y a las principales características de AVX-512, las extensiones se clasifican del siguiente modo: AVX-512 Foundation: expande la mayoría de instrucciones AVX de 32 y 64 bits con el esquema de codificación EVEX para soportar los registros de 512 bits, las operaciones con

24 14 CAPÍTULO 2. ESTADO DEL ARTE máscaras, la difusión de parámetros y las excepciones de control y redondeo empotradas. AVX-512 Conflict Detection Instructions (CDI): añade detección de conflictos eficiente para permitir que más bucles puedan ser vectorizados. AVX-512 Exponential and Reciprocal Instructions (ERI): operaciones exponenciales y recíprocas diseñadas para ayudar en la implementación de operaciones trascendentes, como por ejemplo la función de logaritmo. AVX-512 Prefetch Instructions (PFI): soporte para prefetches. En cuanto a las características técnicas, se resumen en los siguientes puntos: 32 registros vectoriales de 512 bits de ancho bajo la nomenclatura ZMM0-ZMM31. 8 registros dedicados a las máscaras, lo cual es de especial trascendencia para las instrucciones gather y scatter. Operaciones de 512 bits sobre datos empaquetados enteros y de punto flotante. Los programas podrán entonces empaquetar en los nuevos registros de 512 bits cualquiera de las siguientes combinaciones de datos: 8 datos en punto flotante de precisión doble, o 16 datos en punto flotante de precisión simple, u 8 enteros de 64 bits o 16 enteros de 32 bits. Esto permitirá el procesamiento del doble de elementos que el AVX/AVX2 con una sola instrucción y cuatro veces el de SSE. Es interesante resaltar que Intel R AVX-512 ofrece un nivel de compatibilidad con AVX muchísimo mayor que las transiciones anteriores sobre el ancho de las operaciones. A diferencia de lo que ocurre con SSE y AVX, que no se pueden mezclar sin penalizaciones en el rendimiento, la mezcla de instrucciones AVX y AVX-512 es posible sin penalización alguna. Los registros YMM0- YMM15 de AVX se mapean en los registros ZMM0 ZMM15 de AVX-512 del mismo modo que se mapeaban los registros SSE sobre AVX. Por lo tanto, en procesadores que soporten AVX-512, las instrucciones AVX y AVX2 operarán en los 128 o 256 bits inferiores de los primeros 16 registros ZMM.

25 Capítulo 3 Metodología Las ideas que surgieron a la hora de definir el anteproyecto del presente Proyecto Final de Carrera lo describían claramente como un trabajo con una fuerte carga de análisis. Comprendía desde el análisis de todas y cada una de las herramientas a utilizar, hasta el análisis de cada resultado, cada gráfica elaborada, cada bloque básico implicado y cada línea de código que supusiera un objeto de interés sobre el que adentrarse. Por este motivo no se aplicó una estrategia específicamente etiquetada que seria propia de un trabajo vinculado a la rama de ingeniería del software. Ciertamente, parte de este trabajo consistía en desarrollar un simulador sobre el que ejecutar las aplicaciones, con el objetivo de obtener más estadísticas aparte de aquellas conseguidas gracias a las herramientas ya disponibles para los ingenieros de Intel R. Sin embargo, incluso el tomar la decisión de incorporar el desarrollo de este simulador como una extensión de otro ya existente, supuso un fuerte trabajo de análisis para tratar de reciclar la mayor cantidad de información ya disponible. Esta información se encontraba almacenada, en su mayor parte, sobre una gran variedad de estructuras y clases que evitaron, ya no solo no reinventar la rueda, sino también impedir sobrecargar al simulador con operaciones y tareas que eran necesarias y que por supuesto ya se estaban realizando. En este capítulo, se describirá la forma de trabajo, es decir aquellas actividades que supusieron una importante parte para la consecución del trabajo, cómo se distribuyeron todas las tareas a realizar y el tipo de metodología usada cuando se procedió al desarrollo de la nueva extensión del simulador Plan de trabajo Al plan de trabajo diseñado inicialmente y presentado en el anteproyecto se le aplicaron modificaciones sin que ello repercutiese en el computo total de horas. A continuación se presenta la planificación final y las justificaciones sobre los cambios en el caso de haberlos. Fase 1: Selección y caracterización de benchmarks 1. Selección del conjunto de benchmarks sobre los que realizar el estudio a partir de los disponibles, como NPB, Polyhedron, PARSEC, etc. 2. Compilar las aplicaciones de los benchmarks para la arquitectura x86 con la extensión AVX- 512 para la obtención de una caracterización inicial. Utilizando los compiladores disponibles, 15

26 16 CAPÍTULO 3. METODOLOGÍA como ICC e IFORT, se realiza la compilación del conjunto de aplicaciones seleccionadas utilizando aquellas opciones que permitan realizar optimizaciones y vectorización del código. Implica un pequeño análisis mediante el parsing del informe generado, para tener una aproximación inicial al comportamiento de cada aplicación. Esta fase se redujo a la selección y posterior caracterización de las aplicaciones. El motivo por el que no se realizó una criba inicial radica en que al principio, sin más datos que los disponibles estáticamente con el informe del compilador, la mera descripción de la aplicación no parecía suficiente para descartar unas u otras. Era más interesante quedarnos con todas las disponibles y, a partir de diferentes estadísticas, tomar decisiones sobre cuáles analizar según las soluciones software o hardware a aplicar. Fase 2: Recopilación y análisis de información sobre la ejecución vectorial de los programas de prueba 1. Determinación del grado de vectorización de los programas: Usando el emulador Pin/SDE, se obtiene el número de instrucciones ejecutado por cada programa y se determina el grado de vectorización de los mismos. 2. Recopilación de información sobre la jerarquía de memoria: Usando el emulador CMP$im, se obtiene la tasa de fallos de los diferentes niveles de la jerarquía de memoria para cada uno de los programas de prueba. 3. Determinación del grado de utilización de la unidad vectorial: En este apartado se desarrollará un núcleo sencillo basado en la arquitectura del coprocesador Intel R Xeon Phi TM sobre el simulador CMP$im, que permita obtener datos estadísticos para así determinar el grado de utilización de la unidad vectorial. La única modificación planteada en esta fase consistió en realizar el desarrollo del simulador de un core con arquitectura vectorial embebido dentro del simulador de cache CMP$im. El motivo radica en que el simulador de cache proporciona mucha información de interés que puede utilizarse para la simulación de la arquitectura. Además, contiene multitud de estructuras y clases que se pueden utilizar a la vez que se introduce el código sobre el esqueleto correspondiente a la simulación de cache. En primer lugar se llevaría a cabo una exhaustiva fase de análisis sobre CMP$im, para conocer en profundidad tanto la configuración de ficheros del simulador, como las estructuras y clases usados, aparte del funcionamiento específico de la simulación de la cache. Los objetivos principales eran reutilizar estructuras, esquemas y clases ya presentes, saber de qué modo introducir el código correspondiente al simulador de la arquitectura para no entorpecer la simulación ya hecha y poder hacer uso de las estadísticas recopiladas. Una vez conocidas las características principales mencionadas, había que proceder a la fase de desarrollo de la extensión correspondiente a la simulación de la arquitectura vectorial. La metodología más apropiada para desarrollarlo sería incremental. Era preciso en todo momento que el simulador fuese funcional. Por ello, cada vez que se incorporase una nueva funcionalidad, esta debía protegerse con las macros correspondientes. Ademas se tenía que comprobar que todo seguía funcionando correctamente antes de proceder a la incorporación del siguiente incremento. Cada una de las funcionalidades se iba a discutir y diseñar en reuniones semanales, de manera que al final de cada semana se tendrían tanto los progresos obtenidos como las dificultades encontradas. Si todo estaba correcto, se tomaban las decisiones oportunas sobre las siguientes funcionalidades a desarrollar.

27 3.1. PLAN DE TRABAJO 17 Fase 3: Determinación de cuellos de botella en la ejecución vectorial y propuesta de soluciones 1. Selección de un subconjunto de aplicaciones numéricas de entre todos los benchmarks seleccionados, teniendo como referencia las estadísticas recolectadas. Para la selección se tomaron como criterio tanto la relación entre las versiones escalar y vectorial, así como el desglose de ciclos de la aplicación según las dependencias ocasionadas durante la ejecución. 2. Con la información recolectada en los puntos anteriores se determinará cuáles son las regiones del código que tienen un bajo uso de la unidad vectorial. Se estudiará a posteriori cuál es el motivo: si se trata de una falta de vectorización, si se produce por límites en la microarquitecura u otros. 3. Adicionalmente, se propondrán mejoras hardware y/o software encaminadas a aumentar el rendimiento de estas regiones con bajo uso de la unidad vectorial. En esta fase, se incluyó la selección de las aplicaciones, ya que a estas alturas están completamente caracterizadas, de manera que la información disponible permite tomar decisiones basándonos exclusivamente en el comportamiento de las mismas.

28

29 Capítulo 4 Herramientas En este capítulo se introducen las características principales de las herramientas más significativas usadas a lo largo del proyecto Pin Pin 1 es una herramienta de código abierto desarrollada por la empresa Intel R, destinada a la instrumentación de aplicaciones [pin]. Se denomina herramienta de Instrumentación Binaria Dinámica porque la instrumentación se realiza en tiempo de ejecución (JIT, Just in Time): No requiere que la aplicación a instrumentar se tenga que recompilar. Permite instrumentar programas que generan código dinámicamente. Se puede adherir a procesos que ya se estuvieran ejecutando. Proporciona una extensa API para escribir aplicaciones tanto en C, C++ como ensamblador, denominadas pintools, que permitirán instrumentar aplicaciones, ya sean estas single-threaded o multi-threaded, compiladas para las arquitecturas IA-32 (x86 32-bit), IA-32E (x86 64-bit) y procesadores Itanium R. Dicha API permite al programador abstraerse de todas las peculiaridades de la arquitectura para la que se haya compilado el binario. Por tanto, podrá utilizar información de contexto, tales como el contenido de los registros o las direcciones de acceso a memoria, pasándola como diferentes parámetros dentro del código que el proceso de instrumentación inserta en el binario. Además, Pin salva y recupera el contenido de los registros que se utilizaran en dicho código, de manera que no influyan en la ejecución normal del programa instrumentado. Fue utilizada a la hora de estudiar y modificar el simulador que se va a utilizar para simular las aplicaciones numéricas Pintools Una pintool, sea cual sea su funcionalidad, constará de dos secciones fundamentales en su código: 1 No es un acrónimo. 19

30 20 CAPÍTULO 4. HERRAMIENTAS Instrumentación: contendrá las instrucciones necesarias que indiquen a Pin qué información se quiere recoger del código que se está ejecutando, como códigos de registro, hilos en ejecución o contador de programa, entre otros. También indica en dónde se quieren insertar las llamadas a las funciones y procedimientos que harán uso de toda la información recogida. Análisis: contendrá la definición de todas las funciones y procedimientos que tratarán la información recogida en la sección de instrumentación. Además de estas dos secciones, también contendrá, como cualquier aplicación de C o C++, la función main y, como novedad, un procedimiento denominado Fini que es invocado por Pin cuando la aplicación termina. El motivo por el que se tiene que desarrollar este último procedimiento, reside en que una vez hemos dado el control a Pin, no retorna a la función main para terminar. Pin proporciona una amplia batería de pintools con diferentes funcionalidades. En el Listado 4.1 (pág. 20) se muestra el código de una pintool denominada inscount0 que cuenta el número de instrucciones ejecutadas de una aplicación. Se encuentra dentro de la extensa batería de pintools de ejemplo que acompañan a la herramienta. Es una muestra perfecta de la estructura más común de una pintool, en donde se aprecian tanto las secciones principales descritas, como la definición de knobs, opciones de la pintool y otras funciones de interés. 1 # include <iostream > 2 # include <fstream > 3 # include " pin.h" 4 ofstream OutFile ; 5 static UINT64 icount = 0; 6 7 // Seccion de analisis 8 VOID docount () { icount ++; } 9 10 // Seccion de instrumentacion 11 VOID Instruction ( INS ins, VOID * v){ 12 INS_InsertCall ( ins, 13 IPOINT_BEFORE, 14 ( AFUNPTR ) docount, 15 IARG_END ); 16 } KNOB < string > KnobOutputFile ( KNOB_MODE_WRITEONCE, 19 " pintool ","o", 20 " inscount. out ", 21 " specify output file name "); VOID Fini ( INT32 code, VOID * v){ 24 OutFile. setf ( ios :: showbase ); 25 OutFile << " Count " << icount << endl ; 26 OutFile. close (); 27 } INT32 Usage (){ 30 cerr << " This tool counts the number of dynamic instructions executed " << endl ; 31 cerr << endl << KNOB_BASE :: StringKnobSummary () << endl ; 32 return -1; 33 } int main ( int argc, char * argv []) { 36 if ( PIN_Init (argc, argv )) return Usage (); 37 OutFile. open ( KnobOutputFile. Value (). c_str ()); 38 INS_AddInstrumentFunction ( Instruction, 0); 39 PIN_AddFiniFunction ( Fini, 0); 40 PIN_StartProgram (); 41 return 0; 42 } Listado 4.1: Código de ejemplo de la pintool inscount0

31 4.1. PIN 21 Analizando más en detalle el Listado 4.1 se observan llamadas a funciones que forman parte de la gran API proporcionada por pin: PIN Init: inicializa Pin con los argumentos de entrada. Devuelve false si hay errores. INS AddInstrumentFunction: registra qué función se encargará de la instrumentación a nivel de instrucción. PIN AddFiniFunction: registra qué función se invocará antes de que la aplicación instrumentada termine. PIN StartProgram: arranca la ejecución de la aplicación. No se retorna. INS InsertCall: registra qué función se ha de llamar cuando se encuentren instrucciones candidatas a instrumentar. Entre los parámetros que se observan en Listado 4.2 tenemos: ins: instrucción a instrumentar. IPOINT BEFORE: se invocara el análisis antes de que se ejecute. docount: función de análisis. IARG END: fin de parámetros, tanto si los hubiera como si no. Estos parámetros son los que pasarían a la función docount del ejemplo. Véase Listado INS_InsertCall ( ins, 2 IPOINT_BEFORE, 3 ( AFUNPTR ) docount, 4 IARG_END ); Listado 4.2: Función de instrumentación 1 VOID docount ( UINT32 threadid, ADDRINT pc) {... } 2 3 VOID Instruction ( INS ins, VOID * v) 4 { 5 INS_InsertCall ( ins, 6 IPOINT_BEFORE, 7 ( AFUNPTR ) docount, 8 IARG_THREAD_ID, 9 IARG_ADDRINT, INS_Address ( ins ), 10 IARG_END ); 11 } Listado 4.3: Argumentos para la función de análisis Arquitectura software En la Figura 4.1 se observa la arquitectura software de Pin [LCM + 05]. El elemento principal es la máquina virtual, que contiene un compilador just in time que se encarga de recompilar aquellas porciones de la aplicación que se hayan indicado en la sección de instrumentación, sobre las que se inyectará el código de la sección de análisis que le corresponda. El dispatcher es el encargado de lanzar el código recién compilado que almacena en el área denominada code cache. La emulation unit interpreta aquellas instrucciones que no puedan ser ejecutadas directamente, como es el caso de las llamadas al sistema que tienen un tratamiento particular dentro de la máquina virtual. De entre las entradas que alimentan a Pin, se encuentran lógicamente tanto la pintool como la aplicación. En el Listado 4.4 se observa el esqueleto general de la invocación.

32 22 CAPÍTULO 4. HERRAMIENTAS Figura 4.1: Arquitectura software de Pin 1 pin [ pin_opts ] -t pintool. so [ pintool_opts ] -- app [ input ] Listado 4.4: Ejemplo de invocación de Pin 4.2. CMP$im CMP$im (Chip Multi-Processor Cache Simulator) un simulador de cache desarrollado por Intel R y orientado a chips multiprocesador, cuyo objetivo es analizar el rendimiento de memoria de aplicaciones tanto single-threaded, multi-threaded como multi-program. Fue desarrollada sobre Pin, por lo que fundamentalmente es una pintool, aprovechando el perfil de Pin como herramienta de instrumentación binaria dinámica, que suponía una alternativa frente a otros métodos de simulación como trace-driven basado en trazas [JCLJ06][JCLJ08]. Es interesante destacar que CMP$im es una herramienta muy rápida y fácil de utilizar. Además proporciona una gran cantidad opciones, tanto estáticas de forma #define MACRO, como dinámicas (knobs) de forma -knob valor, que la hacen flexible. Por ello permite configurar al detalle el sistema de memorias cache que van a participar en la simulación. Entre estas opciones se destacan las siguientes: Número de niveles de cache. Número de caches por nivel. Número máximo de threads que se pueden lanzar con la aplicación. Políticas de escritura y reemplazo. Características particulares para cada cache, como tamaño, asociatividad, latencia, tamaño de línea, etc. Latencia de la memoria principal, aunque no se simule.

33 4.3. BENCHMARKS 23 TLBs. Inclusividad o exclusividad. Una vez compilada la pintool con las macros deseadas, se lanza la simulación del mismo modo especificado en el Listado 4.4 (pág. 22). La salida se compone de un informe con estadísticas muy detalladas, que proporcionan tanto información general de la aplicación, como particular desglosada por hilos de ejecución y por nivel de cache. Entre ellos, se muestran datos relativos a: Número total de instrucciones ejecutadas y desglosadas por hilos. Estimación 2 del número de ciclos, donde la latencia de cada instrucción es, por defecto, de un ciclo, añadiendo al total la latencia total generada por los accesos a memoria. Número de accesos, aciertos, fallos y tasas de fallo desglosadas por nivel de cache y tipo de acceso (load, store, write back, etc.). Esta herramienta fue utilizada como base para desarrollar el núcleo del coprocesador Intel R Xeon Phi TM. Imágenes cortesía de Intel R Benchmarks El benchmarking es una técnica consistente en medir el rendimiento de un sistema o un componente del mismo, con el objetivo de realizar una comparativa con otro sistema similar de modo que se tenga una referencia base sobre la que trabajar. De este modo, se podría saber si la máquina obtiene buenos resultados o no, de cara a utilizarlos para el fin que convenga. Aplicado en el campo de la informática consistiría en la ejecución de aplicaciones específicamente diseñadas para medir el rendimiento de una máquina o de uno de sus componentes. Por ello, de cara a este trabajo el uso de benchmarks software constituye una piedra angular para poder determinar el grado de utilización efectiva de la unidad vectorial de un procesador. Los benchmarks se pueden clasificar en diferentes categorías. Veamos una posible clasificación: Benchmarks basados en el nivel del rendimiento que miden: Benchmarks de bajo nivel o nivel componente: se encargan de medir directamente un componente específico del sistema como, por ejemplo, la memoria RAM, la tarjeta gráfica o el procesador. Benchmarks de alto nivel o nivel sistema: evalúan el rendimiento global de una máquina. Este tipo es interesante para comparar sistemas que se basan en arquitecturas distintas. Benchmarks basados en el código que los componen: Benchmarks sintéticos: creados específicamente combinando diferentes funciones del sistema a probar, en las proporciones que los desarrolladores estiman oportunas. El objetivo es conseguir medir determinados aspectos del sistema. Esta descripción se asocia rápidamente a los benchmarks de tipo bajo nivel ya que, por ejemplo, para medir el rendimiento del funcionamiento del disco, se pueden incluir funcionalidades de lectura, escritura o búsqueda de datos en disco en el benchmark. 2 No implementa ningún mecanismo que haga uso del paralelismo de instrucciones (ILP).

34 24 CAPÍTULO 4. HERRAMIENTAS Benchmarks de aplicación: hacen uso de aplicaciones reales. En este caso los desarrolladores del benchmark pueden tener interés en utilizar determinadas aplicaciones que realizan funciones enfocadas a una industria concreta o a un determinado tipo de producto. En este caso, se asocian con los benchmarks de alto nivel ya que este tipo de aplicaciones miden el rendimiento global del sistema, pudiendo analizar cómo contribuye cada componente al dicho rendimiento. Independiente de esta clasificación, se pueden tipificar siguiendo otros patrones más específicos. Por ejemplo, existen benchmarks que sirven para medir el rendimiento de máquinas con múltiples núcleos en su procesador o con múltiples procesadores. Existen otros benchmarks para medir la respuesta de las consultas sobre una base de datos. Existen una gran cantidad de benchmarks disponibles hoy en día, entre los cuales podemos mencionar los siguientes a modo de ejemplo: Whetstone: considerado el padre de los benchmarks sintéticos, fue creado en el Laboratorio nacional de Física de Inglaterra. Su objetivo inicial era servir como test para el compilador ALGOL 60 aunque hoy en día forma parte de otros benchmarks. 3DMark: benchmark sintético creado por la compañía Futuremark Corporation, con el objetivo de medir la capacidad de rendering sobre gráficos 3D que tiene la GPU de una máquina, así como la capacidad de procesamiento de la CPU. Ciusbet: creado por Ciusbet. Es un benchmark que se compone de un gran número de pruebas para probar diferentes componentes de una máquina, como la memoria cache, la CPU, el disco duro, etc. FurmKar: benchmark sintético que mide el rendimiento de una tarjeta gráfica al través de la ejecución de un algoritmo de renderizado de pelaje. Su peculiaridad es que, al tratarse de un algoritmo que somete a la GPU a un nivel de estrés muy fuerte, permite medir muy bien la capacidad de aguante y estabilidad de la tarjeta. El conjunto de benchmarks que se presenta a continuación es una muestra continente de variedad de aplicaciones numéricas usadas comúnmente en el ámbito de la computación de alto rendimiento. Todos los programas se caracterizan por ser o bien Free software, o bien Open source Polyhedron Fortran Benchmarks Polyhedron[pol] es un paquete de 17 programas escritos en Fortran 90, diseñados para comparar el rendimiento de los diferentes ejecutables generados por distintos compiladores. Todos los programas se pueden descargar y hacer uso de ellos según convenga. El paquete que actualmente está disponible para descarga, se denomina pb11. Sin embargo, para el presente trabajo hicimos uso de 15 aplicaciones del antiguo repertorio, denominado pb05, debido a que el conjunto de datos de entrada permitía una mayor rapidez de cara a la simulación completa de los programas. El nuevo benchmark tiene conjuntos de datos que ralentizaban demasiado su simulación. Las 15 aplicaciones son las siguientes: ac armod air capacita channel doduc fatigue gas dyn induct linpk mdbx nf protein test fpu tfft

35 4.3. BENCHMARKS Mantevo 1.0 Mantevo[man] es un benchmark que proporciona una interesante variedad de aplicaciones clasificadas en: Miniapplications: partiendo de la idea de que la medición del rendimiento de las aplicaciones viene determinada por una combinación de diferentes opciones, proporcionan una aproximación excelente para explorarlas. Las opciones mencionadas englobarían las siguientes: el compilador usado, el hardware de la máquina a medir, el algoritmo, el entorno de ejecución, el uso de miniaplicaciones, definidas como pequeños proxies autocontenidos para aplicaciones reales, etc. Minidrivers, pequeñas aplicaciones que sirven para simular el funcionamiento de diferentes controladores. Application proxies, aplicaciones parametrizables cuyo objetivo es simular el comportamiento de aplicaciones a gran escala. Todas ellas realizan mayoritariamente operaciones en coma flotante para, por ejemplo, resolver ecuaciones diferenciales en derivadas parciales tanto implícitas como explícitas, simular modelos de dinámica molecular que implican operaciones sobre vectores, etc. Las aplicaciones de que se compone son las siguientes: CloverLeaf CoMD HPCCG-200 minighost minife minimd minixyce De los tres conjuntos de ficheros de entrada disponibles para realizar las simulaciones, nos hemos quedado con los mediums, con el objetivo de tener los resultados en tiempos razonables ASC Sequoia Benchmark Codes Los investigadores del Laboratorio Nacional Lawrence Livermore (LLNL), con motivo del programa ASC (Advanced Simulation and Computing) de la Administracion Nacional de Seguridad Nacional (NNSA) de Estados Unidos, llevan a cabo multitud de simulaciones sobre el supercomputador de IBM denominado Sequoia. Dentro de los recursos que están disponibles en la plataforma online dedicados a este programa y a los trabajos realizados sobre el supercomputador, se encuentra todo un interesante repertorio de aplicaciones[seq]. De entre todas ellas, y dado que solo se iba a simular uno de los núcleos del coprocesador Intel R Xeon Phi TM, se seleccionaron solamente las aplicaciones correspondientes a la sección Tier 3, que se caracterizan por ser single-threaded: UMTmk IRSmk SPhotmk Crystalmk NAS Parallel Benchmarks Los NAS Parallel Benchmarks[nas] (NPB) son un conjunto de aplicaciones destinadas a la medición del rendimiento de supercomputadores paralelos. Fueron desarrolladas por la división NAS (NASA Advanced Supercomputing). Inicialmente, en la especificación NPB 1, estaba conformado por 5 kernels y 3 pseudo aplicaciones. Más adelante fue extendida para incluir nuevos benchmarks sobre mallas adaptativas, aplicaciones E/S paralelas y redes computacionales. Se utilizó la versión que contiene las siguientes aplicaciones:

36 26 CAPÍTULO 4. HERRAMIENTAS BT CG DC EP FT IS LU MG SP UA Los diferentes inputs se categorizan en las siguientes clases: Class S: para pequeñas pruebas. Class W : destinada a estaciones de trabajo. Classes A, B, C : test de mayor tamaño, cada uno de los cuales es aproximadamente 4x superior al anterior. Classes D, E, F : en este caso son entradas muy grandes, del orden de los 16x de incremento entre un test y el siguiente. Para el presente caso, era suficiente utilizar la clase W, puesto que el número de instrucciones ejecutadas se encontraba en el orden de magnitud de los otros benchmarks seleccionados SPEC CPU 2006 La Standard Performance Evaluation Corporation (SPEC), es una organización sin ánimo de lucro cuyo objetivo es producir, establecer, mantener y promocionar un paquete estándar de benchmarks para medir el rendimiento de diferentes máquinas. En este sentido, se dispone del conjunto de benchmarks denominado SPEC CPU2006 diseñado para proporcionar una medida comparativa, con el objetivo de analizar el rendimiento conseguido después de realizar cálculos intensivos sobre una máquina. El conjunto de aplicaciones que lo conforman fueron desarrolladas basadas en aplicaciones de usuario reales. Los resultados que se obtengan serán fuertemente dependientes del procesador, la memoria y el compilador utilizado. Dado que el presente trabajo está centrado en el estudio efectivo de un procesador vectorial, nos centramos fundamentalmente en uno de los dos suites en que se divide: CFP2006, que sirve para medir el rendimiento de las operaciones en punto flotante. El otro suite, CINT2006 está enfocado a operaciones enteras. Las aplicaciones son las siguientes: 410.bwaves 416.gamess 433.milc 434.zeusmp 435.gromacs 436.cactusADM 437.leslie3d 444.namdP 447.dealII 450.soplex 453.povray 454.calculix 459.GemsFDTD 465.tonto 470.lbm 481.wrf 482.sphinx3 Como datos de entrada, están disponibles los siguientes: all: común para todos los benchmarks, se usa en caso de ser necesario. ref : es el conjunto de datos real y completo. test: entrada para tests más sencillos. train: tests más grandes. Para nuestras necesidades, basta con usar test.

37 4.4. COMPILADORES Compiladores Los compiladores son una parte fundamental de este trabajo, puesto que son los responsables de generar el código necesario con el que se trabajara después. Si bien todos los elementos expuestos en este capítulo son indispensables para conseguir las sinergias que permitirán completar todos los objetivos definidos en la Sección 1.1 (pág. 2), los compiladores se alzan con la responsabilidad suprema. En el caso particular de los optimizadores de que constan, son responsables de generar un código adecuado con el que se pueda partir, trabajar y mejorar en caso necesario. Si no fuera así, ningún resultado tendría la fiabilidad suficiente. Por estos motivos trabajamos con los compiladores Intel R C++ Compiler e Intel R Fortran Compiler. Una de las ventajas principales de trabajar con estos compiladores y de realizar este trabajo en la propia empresa Intel R, es que se disponía en todo momento de la última versión de los compiladores. Por tanto, este trabajo servía también como depurador de los cambios introducidos en cada versión. La versión utilizada en las simulaciones se corresponde con la de Mayo de Para las simulaciones realizadas durante los meses de Junio y Julio utilizamos la misma para mantener la coherencia en los resultados. Utilizar otro implicaría obtener mejoras que no recaerían sobre los cambios en configuraciones utilizadas al simular, sino en las propias mejoras del compilador. El código generado al compilar contiene las extensiones AVX-512 descritas en la Sección 2.4 (pág. 13). Asimismo, mientras los compiladores están disponibles en multitud de plataformas, en este trabajo se usó la versión para Linux. Los compiladores fueron usados a la hora de construir la caracterización de todas y cada una de las aplicaciones, y para tener los ejecutables disponibles en el momento de proceder a la simulación con la versión de CMP$immodificada ICC El compilador Intel R ICC permite generar código sobre arquitecturas IA-32, Intel R 64 e Intel R MIC (Multiple Integrated Core) [intc], disponibles para los sistemas operativos Mac OS X, Linux y Microsoft TM Windows. Contiene soporte para la vectorización de aplicaciones, pues puede generar instrucciones de los repertorios SSE (Streaming SIMD Extensions), SS2, SSE3, SSSE3 (Suplemental Streaming SIMD Extensions), SSE4, AVX(Advanced Vector Extensions) y AVX2. Las versiones internas del compilador, además, permitían generar código AVX-512. El vectorizador automático es un componente importante del compilador, ya que utiliza automáticamente instrucciones SIMD (Simple Instruction Multiple Data) de los repertorios de instrucciones mencionados anteriormente. Se encarga de detectar aquellas operaciones en el programa que se pueden vectorizar para explotar el procesamiento automático de las instrucciones de tipo SIMD. El usuario puede ayudar a este módulo mediante el uso de pragmas. Consúltese el conjunto de pragmas disponibles en la Sección 4.5 (pág. 29) ICC tiene multitud de knobs de compilación[intc]. Para este trabajo, el formato de las mismas se corresponde con aquel para Linux. A continuación se mostrarán las opciones más significativas usadas en este trabajo. La línea de compilación tiene el formato mostrado en el Listado 4.5. La opción -no-vec que se observa en la línea de compilación, se usaba para indicar al compilador que no vectorizase. Como se verá más adelante, la versión no vectorizada de las aplicaciones es de interés porque sirven de referencia para compararlas con la versión vectorizada. 1 icc -g - debug inline - debug - info -vec - report6 2 -ansi - alias -O3 -no -prec - div -ipo - static 3 -xknl [-no - vec ] -o <ouput > <inputfiles > Listado 4.5: Línea de compilación

38 28 CAPÍTULO 4. HERRAMIENTAS -g Produce información para depuración simbólica en el fichero objeto. -debug inline-debug-info -vec-report6 -ansi-alias Genera información de depuración mejorada tanto en el caso de código del que se haga inline como en el rastro generado por sucesivas llamadas a funciones. Genera un log con toda la información concerniente a los bucles y bloques que han sido vectorizados, así como de las razones porque otros no lo han sido. Indica al compilador que compile bajo las reglas de aliasability estándares de la ISO de C. -O3 Aparte de las optimizaciones de la opción -O2, incluye prefetching, transformación de bucles y sustitución de escalares. -no-prec-div -ipo -static -xknl -no-vec Permite optimizaciones que, a cambio de divisiones algo menos precisas que las operaciones de división en sí mismas, sustituye estas por multiplicaciones. Por ejemplo, en vez de A/B, se calcularía A*(1/B) Interprocedural Optimization. Indica al compilador que haga inline de las llamadas a funciones que se encuentran en otros ficheros. Un ejemplo sería en aquellas llamadas dentro de bucles. Por defecto el valor es 0, esto es que se dejara al compilador decidir si crear uno o más ficheros objetivo dependiendo de una estimación del tamaño de la aplicación. Impide enlazar con librerías compartidas. En su lugar, enlaza todas las librerías estáticamente. La arquitectura para la que se está generando código es KNL, que incluye AVX-512. Impide al compilador hacer uso del módulo de vectorización. El log resultante de aplicar esta opción no contendría más información que la línea de compilación usada y los ficheros compilados. Tabla 4.1: Knobs soportados por Intel R ICC IFORT El compilador Fortran de Intel R al igual que ICC, permite compilar aplicaciones sobre las arquitecturas IA-32, Intel R 64 y Intel R MIC (Multiple Integrated Core) [intc], disponibles para los sistemas operativos Mac OS X, Linux y Microsoft TM Windows. Igualmente, tiene características análogas al compilador ICC, como es el soporte para la vectorización de aplicaciones, y comparte la mayoría de knobs disponibles. La línea de compilación solo difiere en la opción -fpp, la cual sirve para indicar al compilador

39 4.5. PRAGMAS 29 que corra el preprocesador de Fortran sobre los ficheros fuentes antes de realizar la compilación. El resto permanecen invariantes. 1 ifort - fpp -g - debug inline - debug - info -vec - report6 2 -ansi - alias -O3 -no -prec - div -ipo - static 3 -xknl [-no - vec ] -o <ouput > <inputfiles > Listado 4.6: Línea de compilación 4.5. Pragmas Los pragmas son directivas que sirven para especificar qué tiene que hacer el compilador en determinadas situaciones. Estas instrucciones pueden tener efectos a nivel global o a nivel local. Por ejemplo, para el caso del presente trabajo, puede ser necesario el uso del pragma vector para indicar al compilador que un bucle concreto tiene que ser vectorizado, en cuyo caso es un pragma a nivel local. Los pragmas no forman parte de un lenguaje de programación, ya que no figuran en su gramática, pero algunos lenguajes, como C++ y Fortran, tienen disponible palabras clave para su uso, las cuales son tratadas por el preprocesador. En C++ es #pragma y en Fotran es!dir$. Además, hay que tener en cuenta que los pragmas son dependientes tanto de la máquina como del sistema operativo, aparte de que cada compilador tiene su propio conjunto. Es también posible que la funcionalidad proporcionada por un pragma se consiga con alguna opción particular de compilación. En caso de coincidir en funcionalidad las opciones del compilador con los pragmas, tienen prioridad los segundos. Los pragmas, que se pueden consultar en el Apéndice A (pág. 105), el Apéndice B (pág. 109) y el Apéndice C (pág. 113), están disponibles tanto para procesadores Intel R como de otras empresas, pero es posible que en procesadores Intel R lleven a cabo optimizaciones adicionales. Hay que tener en cuenta que en el caso de Fortran, al hablar de los pragmas, en realidad se habla de directivas y su listado no coincide más que en algunos casos con los de ICC. En el Listado 4.7 y el Listado 4.8, se visualizan las diferencias de sintaxis. Particularmente, los pragmas de ICC tienen la siguiente clasificación: Intel R ICC Specific Pragmas: desarrollados específicamente para trabajar con el compilador Intel R C++. Intel R ICC Supported Pragmas: desarrollados por fuentes externas que, por razones de compatibilidad, son soportados por este compilador. 1 # pragma nombre [ parametros ] Listado 4.7: Sintaxis de los pragmas de ICC 1 (c C! *) ( DEC DIR )$ directiva [ parametros ] Listado 4.8: Sintaxis de las directivas de Fortran 4.6. Herramientas internas Toda empresa dispone de un conjunto de herramientas que han sido desarrolladas por los propios empleados. Estas suelen tener fines exclusivamente internos e incluso de uso restringido en

40 30 CAPÍTULO 4. HERRAMIENTAS la propia organización, al poner su disponibilidad bajo la previa aprobación del jefe del grupo. Intel R es una empresa muy grande, de importante capital y con gran cantidad de recursos disponibles para sus empleados, incluyendo las herramientas desarrolladas por los diferentes grupos de trabajo. Por este motivo, previa autorización, tuve acceso a un conjunto de ellas de cara a facilitarme la tarea durante el desarrollo de este trabajo. Estas herramientas facilitaban el análisis de las aplicaciones, al presentar todos los datos procedentes de los informes generados por diferentes simuladores, como es el caso de CMP$im y otras pintools, y del informe del compilador, de un modo más legible y fácil de estudiar, que lo que un fichero de texto con multitud de números y líneas permite. La herramienta que se usó principalmente permitía, para cada unas de las aplicaciones simuladas, entre otras funcionalidades, las siguientes: Analizar los bloques básicos individualmente. Consultar el código fuente asociado a cada bloque básico. Visualizar el flujo de ejecución del programa. Consultar la distribución de funciones e instrucciones. Sintetizar la información procedente de cada nivel de la cache. Todas ellas se usaron principalmente durante la caracterización de las aplicaciones y a la hora de realizar el diagnóstico software para averiguar las causas del bajo grado de vectorización.

41 Capítulo 5 Arquitectura del Simulador En el presente capítulo se hará un análisis más en detalle del funcionamiento del simulador CMP$im presentado en la Sección 4.2 (pág. 22). Debido a que es una pintool y que esta íntimamente relacionado con la herramienta Pin, habrá detalles de Pin que será necesario exponer para comprender mejor el esquema general del simulador Flujo de ejecución Las dos secciones principales en las que se organiza una pintool, como se describieron en la Sección (pág. 19), son las secciones de instrumentación y de análisis. CMP$im no es una excepción. En la Figura 5.1 (pág. 31) se presenta una visión general de cómo funciona. En primer lugar tenemos la función main, donde se recogen las opciones de configuración de la pintool introducidas por parámetro y tanto la aplicación objeto del análisis como sus datos de entrada. Aquí también se llevan a cabo las inicializaciones pertinentes según la configuración de cache que se haya elegido. Una vez se lanza la simulación no se retorna. Por ello, la finalización de la aplicación está ligada a una función de terminación que sera invocada por Pin en el momento oportuno. Figura 5.1: Diagrama de funcionamiento CMP$im 31

42 32 CAPÍTULO 5. ARQUITECTURA DEL SIMULADOR En segundo lugar tenemos el bloque principal compuesto por las fases de instrumentación y análisis. Pese a que la instrumentación es la fase con la que se inicia, ambas se van intercalando a medida que se avanza en la simulación. Durante esta fase se van insertando las llamadas a las funciones según el tipo de estructura con el que se esté tratando. Estas funciones se encargarán de realizar la simulación de la cache. Asimismo, irán recogiendo todas las estadísticas que se hubieran programado en ellas, como por ejemplo aquellas mencionadas en la Sección 4.2 (pág. 22). En el caso de la simulación de cache se recogen, por ejemplo, el número de aciertos y de fallos, desglosados por hilo, nivel de cache y PC. En el caso de las funciones asociadas a los bloques, se almacenarían el número total de instrucciones ejecutadas por cada bloque desglosadas por cada hilo lanzado por la aplicación. El objetivo es almacenar en las estructuras definidas a tal efecto, toda la información que sea de utilidad para caracterizar la aplicación y estudiar su comportamiento sobre la cache definida. Finalmente, una vez ha terminado la aplicación, se ejecuta la función de finalización. En esta se lleva a cabo todo el volcado de la información almacenada en las diferentes estructuras para su posterior tratamiento. En la Figura 5.2 (pág. 32) se muestra de forma más detallada el flujo de ejecución para simular la cache en un modo denominado modo buffer. Este modo se usa cuando se van almacenando todas las instrucciones de memoria de un bloque antes de simularlas. Las llamadas insertadas en la instrumentación están indicadas con la palabra clave call. La llamada a foo1 se insertaría al detectar una instrucción de carga de memoria; foo2 cuando es de almacenamiento; finalmente, foo3 se insertará al detectar un bloque básico. Para el modo buffer, la función foo3 sería la encargada de iniciar la simulación de instrucciones previamente instrumentadas. Dado que foo3 se ejecutaría antes que foo1 y foo2, la primera vez no habría instrucciones que simular. En las veces sucesivas ya se habrían instrumentado instrucciones de memoria. Por ello, se da la circunstancia de que cada bloque básico lanzaría la simulación de las instrucciones registradas en el bloque anterior. Este bloque podría ser tanto él mismo, en caso de bucle, como otro. Para evitar problemas con la simulación de las instrucciones del último bloque, la función de finalización es una buen candidata donde comprobar si faltan instrucciones de memoria por simular. Figura 5.2: Simulación en modo buffer En la Figura 5.3 (pág. 33) se muestra el flujo de ejecución en modo instrucción a instrucción. En este otro caso las instrucciones se van simulando a medida que se van encontrando, lo cual lo hace más lento que el caso anterior al tener que proceder a realizar todo el trasiego de paso de parámetros para cada instrucción de memoria que se encuentre en el programa.

43 5.2. ESTRUCTURAS Y CLASES 33 Figura 5.3: Simulación en modo instrucción a instrucción 5.2. Estructuras y clases Las estructuras de datos y clases más importantes usadas en el simulador, y que podrían aprovecharse más adelante, son las siguientes: Bloque Básico Rutina Cache Estadísticas BLOQUE BASICO Dentro de cada bloque básico CMP$im almacena los siguientes campos: Direcciones de comienzo y fin: son los PCs de la primera y última instrucciones del bloque. Es muy importante saber dónde empieza un bloque y dónde termina porque, como vimos en el modo buffer descrito en la Sección 5.1 (pág. 31), las instrucciones que se tratan son las del bloque anterior. Si conocemos sus límites, se pueden utilizarlos en adelante a modo de índice. Contador: número de veces que se ejecuta un bloque. Se utiliza como estadística para poder elaborar una lista de bloques básicos ordenados por frecuencia de ejecución. La estructura está instanciada a modo de lista STL. Véase el Listado 5.1 (pág. 33). Hay que tener en cuenta que la instrumentación, pese a estar programada en modo buffer para el caso de los bloques, dentro de pin realmente se realizaba a nivel de traza. Como una traza puede contener varios bloques básicos, en el momento en que se reconoce uno y se inserta en la llamada a la función de análisis correspondiente, no se tiene en cuenta si está solapado con otros bloques. En la Figura 5.5 (pág. 34) se presenta la problemática. 1 list < const BBInfo * > BBInfoList ; Listado 5.1: Lista STL de Bloques Básicos

44 34 CAPÍTULO 5. ARQUITECTURA DEL SIMULADOR Figura 5.4: Ejemplo de bloque básico Figura 5.5: Proceso de descubrimiento de bloques Imaginemos que tenemos un caso de bloques básicos como el de la Figura 5.4 (pág. 34). Antes de reconocer los dos bloques por separado, Pin identifica un único bloque de instrucciones hasta el siguiente salto. En la Figura 5.5 (pág. 34) se ha denominado BBL 0. Durante la instrumentación se insertaría entonces una entrada en la lista con la información del BBL 0 y, en caso de simulación en modo buffer, su correspondiente call a la función de análisis tal y como se ejemplificaba en la Figura 5.2 (pág. 32). A continuación, encontraría una instrucción de salto a una posición dentro BBL 0. Es entonces cuando indicaría que hay dos bloques, BBL 1 y BBL 2, que se insertarían en la lista y sobre los cuales se agregarían los correspondientes call. Ya sea con el nombre BBL 0 o con el nombre BBL 1, ambos comenzarían en la misma instrucción. Además, en el código instrumentado aparecerían dos llamadas a la función de análisis que cuenta el número de veces que se ejecuta el bloque. Dentro de los parámetros de las llamadas a la funciones de análisis se indicaría el elemento de la lista de bloques sobre el que incrementar el contador. Dado que al final de la ejecución ambas entradas tendrán la misma cantidad en el campo Contador, sería posible añadir un tratamiento posterior que mejorara las estadísticas finales. Situaciones como esta son inevitables, ya que Pin no puede predecir inicialmente que va a haber un salto a una instrucción intermedia hasta que ésta tenga lugar. Por tanto, almacenar todos los bloques que encuentre inicialmente es necesario para, al final de la simulación, realizar las intersecciones y uniones pertinentes para imprimirlos en el informe final. RUTINA El almacenamiento de todas las rutinas ejecutadas permite averiguar, para un bloque básico, en qué rutina se encuentra. Esta información, en posteriores análisis, participa en la identificación del código fuente correspondiente a un bloque en concreto. Los campos de más relevancia son los siguientes. Nombre: de cara al código fuente, es un buen modo de identificar todo el código ensamblador perteneciente a una rutina. Hay que tener en cuenta que habrá llamadas sobre las que se haya hecho inline. Direcciones de comienzo y fin: son los PCs de las instrucciones inicial y final de la rutina.

45 5.2. ESTRUCTURAS Y CLASES 35 Ayudarán a indexar los bloques básicos que encierran. La estructura estaba instanciada como un mapa STL indexado por el contador de programa de la primera instrucción de la rutina. Véase el Listado 5.3 (pág. 36). La documentación de Pin no aconseja averiguar la última instrucción de la rutina. En el caso de que hubiera varios puntos de salida (return) no se asegura que la ultima instrucción sea realmente la esperada. 1 map < UINT64, RTNInfo * > RTNInfoMap ; Listado 5.2: Mapa STL de Rutinas CACHE La clase CACHE contiene toda la descripción de las características y comportamiento aproximados de lo que sería una cache real. En ella se centran las rutinas más importantes que simulan el comportamiento en memoria de una aplicación. Los atributos de que consta son los propios de una memoria cache tales como tamaño, asociatividad, tamaño de la línea, latencia, inclusividad o exclusividad, política de reemplazo, etc. Hay que destacar que, tal y como estaba organizado el simulador, pese a que es una clase que podría coexistir en sí misma, estaba diseñada como clase derivada de una clase padre denominada ESTADISTICAS. La función más importante se denominaba CacheAccess. Los parámetros principales de esta función son: PC: instrucción responsable del acceso. Address: dirección de la línea a la que se quiere acceder. Tipo de acceso: es un enumerado que acepta cualquiera de los siguientes tipos de acceso: LOAD: acceso de lectura. STORE: acceso de escritura. SOFTWARE PREFETCH: software prefetch. READ FOR WRITE: caso especial en el que se accedería al siguiente o siguientes niveles de cache para traer una línea que ocasionó un fallo y sobre la que se quiere escribir. Este tipo de accesos no se verían en una configuración de cache con política de escritura write-through. WRITE BACK: si la política de reemplazo ocasiona que se sustituya un bloque donde el dirty-bit se encontraba a uno, se producirá un acceso de este tipo para escribir el bloque antes de que se produzca el reemplazo. WRITE THROUGH: en una configuración con política de escritura write-through, los accesos a los niveles posteriores al más próximo a la CPU tendrían este etiquetado. INVAL: acceso que se produce en todas las caches del mismo nivel para invalidar un dato que podría haberse modificado. BACK INVAL: acceso en casos de caches inclusivas. Cuando se producen desalojos de bloques en niveles lejanos al procesador, hay que regresar a los más cercanos para invalidarlos si fuera necesario, manteniendo la inclusividad. Resultado: parámetro de entrada/salida que recoge si ha habido acierto o fallo.

46 36 CAPÍTULO 5. ARQUITECTURA DEL SIMULADOR Figura 5.6: Punteros a objetos cache 1 void CacheAccess ( INT64 PC, ADDR address, enum TYPE_ACCESS typeaccess ); Listado 5.3: Función de acceso a la cache En la definición de la función, hay llamadas sucesivas a otras funciones miembro privadas que se encargaban de lo siguiente: Examinar el mapa de su cache asociado. Averiguar si existía la línea solicitada. Proceder a realizar los reemplazos necesarios en caso de que se tratara de un fallo. Lanzar las órdenes de invalidación sobre el resto de caches. Actualizar las estadísticas de hit o miss para el PC responsable de la solicitud. El número de objetos creados de la clase CACHE dependen de la configuración pasada por parámetro a la pintool. En la Figura 5.6 (pág. 36) se presenta el modo de almacenar todos los punteros a los diferentes objetos cache creados a través de una matriz. CMP$im soporta aplicaciones multi-hilo. En la configuración inicial es posible indicar cuántos threads acceden a determinado objeto de cache. Es un requerimiento que el número de hilos esperados por la pintool sea potencia de dos, aunque los creados por la aplicación no lo sean. Cada vez que un hilo quisiera acceder a su objeto de cache, existe una función que marca la correspondencia entre el id del thread y el índice de la cache. Una vez obtenido el puntero, se invocaría la función CacheAccess. ESTADISTICAS La clase que almacena las estadísticas es una clase de la que posteriormente hereda la clase CACHE. Se encarga simplemente de proveer, para cada uno de los niveles de cache, de los atributos y funciones miembro necesarios para contener estadísticas y volcarlas al final de la ejecución. La mayoría de estos atributos son a su vez estructuras. Todos están indexados por thread y PC en este orden. Entre los datos más importantes que se contabilizan figuran los siguientes: accesos, aciertos, fallos, tipos de acceso, fallos forzosos, desalojos, víctimas e invalidaciones.

47 5.3. PARÁMETROS DE EJECUCIÓN Parámetros de ejecución La línea utilizada para simular todas las aplicaciones se presenta en el Listado 5.4 (pág. 37). Los knobs que en figuran en este listado son: -cache: sirve para configurar los distintos niveles de cache. Habrá un knob por cada nivel. 1 - cache < identificador >: < tamano >: < tamano linea >: < asociatividad >: < bancos > En nuestro caso, vamos a configurar 2 niveles, siendo el primero de ellos una cache de datos L1 y el segundo una caché unificada L2. Los identificadores están predefinidos, por tanto serán DL1 y UL2. La DL1 será de 32KB con líneas de 64B y asociatividad 8. La UL2 será de 1KB con líneas de 64B y asociatividad 16. -tlb: para configurar los distintos niveles de TLB. 1 - tlb < identificador >: < lineas >: < tamano pagina >: < asociatividad > Configuraremos dos niveles de TLB de datos. En este caso, de nuevo los identificadores están predefinidos, por tanto seran DTLB y DTLB2. La DTLB tendrá de 64 lineas y un tamaño de página de 4KB, permitiendo mapear 256KB. La DTLB2 tendrá 256 entradas y páginas de 4KB para mapear 1MB. -dl1lat: la latencia de la DL1 será 4. -ul2lat: la latencia de la UL2 será 16. -dtlblat: la latencia de la DTLB será 0. -dtlb2lat: la latencia de la DTLB2 será 6. -inclusive: se modelarán caches inclusivas, por tanto se activará indicándolo con un 1. -threads: no se modelarán aplicaciones multihilo, por tanto se indicará que solo se quiere 1 hilo. La latencia de la memoria principal se ha dejado a su valor por defecto, 350 ciclos. Esto influirá tanto en los fallos de la UL2 como en los de la DTLB2. 1 pin -t cmpsim - cache DL1 :32:64:8:1 2 - cache UL2 :1024:64:16:1 3 - tlb DTLB :64:4096:8 4 - tlb DTLB2 :256:4096:8 5 - dl1lat 4 6 -l2lat dtlblat dtlb2lat inclusive threads 1 -- <app > <input > Listado 5.4: Línea para el lanzamiento de la simulación

48

49 Capítulo 6 Caracterización de benchmarks En este capítulo se presenta la caracterización de los benchmarks descritos en la Sección 4.3 (pág. 23). Se fundamenta en el primero de los objetivos de la Sección 1.1 (pág. 2) que hacía referencia al análisis y clasificación de un conjunto de aplicaciones numéricas en función del grado de vectorización. La caracterización se ha llevado a cabo de la siguiente manera: una vez seleccionada la muestra de benchmarks, se han compilado y se ha extraído el porcentaje de vectorización. Para la generación de este porcentaje se ha hecho uso de una herramienta interna disponible en Intel que instrumentaba de manera muy rápida las instrucciones de una aplicación. Como en este punto era importante cuantificar el numero de instrucciones vectoriales de que se compone, se realizó la siguiente clasificación: instrucciones enteras e instrucciones en punto flotante. Las instrucciones en punto flotante están clasificadas a su vez entre aquellas que operan sobre un único elemento, llamadas escalares, vec 1, y las que lo hacen sobre n elementos, llamadas vectoriales, vec n. Adicionalmente, a partir del informe generado por el compilador, se ha extraído un resumen sobre las causas encontradas por éste para no vectorizar. Esta información se presenta en dos gráficas por cada benchmark, una con el porcentaje de vectorización y otra con las causas de la no vectorización Polyhedron La Figura 6.1 muestra el porcentaje de instrucciones escalares y vectoriales de los benchmarks de Polyhedron. Están ordenados de izquierda a derecha desde los de menor índice de vectorización hasta los de mayor, respectivamente. En la Tabla 6.1 (pág. 40) se adjunta el desglose numérico del que se sostiene la figura mencionada. A la derecha de la gráfica tenemos aplicaciones como channel, linpk y test fpu, cuyos índices de vectorización son óptimos, sobre todo en el caso de channel. En el extremo opuesto tenemos a tfft con el peor ratio de vectorización. Las aplicaciones protein y ace son ejemplos sobre los que no merece la pena centrarse, ya que mayoritariamente tienen instrucciones enteras, las cuales están fuera de ser objetivo de la vectorización. En su lugar, otras aplicaciones como fatigue, doduc, mdbx, nf o induct, parecen interesantes candidatos a ser analizados. Dada la importancia de comprobar el informe generado por el compilador, se presenta la Figura 6.2. El eje de coordenadas izquierdo representa la distribución en porcentajes de las razones más significativas. En él decidimos incluir la distribución de bucles que sí han sido vectorizados. El eje de coordenadas derecho presenta la cantidad real de regiones tratadas por el compilador. Se visualizan con una marca blanca rectangular y sirve de referencia para determinar si la vectoriza- 39

50 40 CAPÍTULO 6. CARACTERIZACIÓN DE BENCHMARKS Figura 6.1: Índice de vectorización de las aplicaciones de Polyhedron ción en realidad ha sido buena. En la leyenda, una de las series se denomina Other. En esta serie se han incluido aquellas que tenían poco peso. En el caso de Polyhedron, se incluyen las siguientes: insufficient computational work, cannot vectorize empty loop, unsupported reduction, conditional assignment to a scalar y statement cannot be vectorized. Conviene resaltar que esta gráfica no deja de ser una representación estática del log del compilador. Existen bucles que, habiendo sido vectorizados, o bien no se ejecutan debido a los datos de entrada, o bien el número de instrucciones que contiene es pequeño, impidiendo que el índice de vectorización sea más favorable. Polyhedron Enteras % Escalares % Vectoriales % channel , , ,23 linpk , , ,18 test fpu , , ,55 induct , , ,30 gas dyn , , ,04 nf , , ,63 capacita , , ,12 air , , ,07 mdbx , , ,87 doduc , , ,93 aermod , , ,59 fatigue , , ,72 ac , , ,75 protein , , ,88 tfft , , ,08 Tabla 6.1: Desglose de instrucciones de las aplicaciones de Polyhedron Se observa que channel está en cabeza en cuanto al número de bucles vectorizados. Hacen un total de 66, cantidad pequeña comparativamente. Pese a ello, el 82 % de dichos bucles está vectorizado permitiendo que la oportunidad de ejecutar una instrucción vectorial aumente. Esto se traduce en un óptimo índice de vectorización (recuérdese la Figura 6.1 (pág. 40)). La aplicación tfft es interesante porque mientras que el índice de vectorización era nulo, los bucles vectorizados

51 6.2. MANTEVO Figura 6.2: Razones para no vectorizar bucles en Polyhedron ocupan el 33 % del total. Se trataría por tanto de bucles que no se ejecutan o que lo hacen con muy poca frecuencia. Por otro lado, mientras que aermod destaca por la cantidad de bucles que contiene, 2597, un 72 % no se ha vectorizado, siendo la razón de peso que los bucles no iteran lo suficiente como para que sea eficiente, low trip count. En air, el número de bucles vectorizados compite con los que no lo han sido. El motivo principal es not inner loop. Cada vez que el compilador se dispone a vectorizar un bucle de entre un conjunto de bucles anidados, para todos los bucles externos registra como motivo que no son bucles internos. En air, se entiende entonces que hay muchos bucles anidados Mantevo 1.0 El índice de vectorización de las aplicaciones de Mantevo visualizadas en la Figura 6.3 (pág. 42) no parecen dar tanto juego como en Polyhedron. La Tabla 6.4 (pág. 43) adjunta el desglose de instrucciones. La aplicación CoMD, que podría ser uno de los candidatos, tiene un 69,35 % de instrucciones enteras. Si bien podría ser estudiado para reducir el número de instrucciones escalares, vec 1, las enteras no parecen dejar mucho margen para conseguir una mejora más significativa. En la Figura 6.4 (pág. 43) con las razones del compilador para no vectorizar, las opciones englobadas en la categoría other son: low trip count, loop was transformed to memset or memcpy y conditional assignment to a scalar. La aplicación CloverLeaf, que presentaba el mejor índice de vectorización con un 73,13 % de instrucciones vec n, aquí se encontraría por detrás de minighost en cuanto a bucles vectorizados. Entre los motivos para no vectorizar, es mayoritaria la razón not inner loop. En Polyhedron ya vimos que no es una razón de peso. Además, dado que es la aplicación con mayor número de bucles, 1127, en realidad se trata de un caso de éxito. En la misma línea tenemos a minighost, con un resultado semejante en cuanto a bucles vectorizados,

52 42 CAPÍTULO 6. CARACTERIZACIÓN DE BENCHMARKS Figura 6.3: Índice de vectorización de las aplicaciones de Mantevo 1.0 Mantevo Enteras % Escalares % Vectoriales % CloverLeaf , , ,13 minighost , , ,65 HPCCG , , ,94 minimd , , ,59 minife , , ,57 minixyce , , ,94 CoMD , , ,22 Tabla 6.2: Desglose de instrucciones de las aplicaciones de Mantevo pero con 294 bucles. Volviendo a su índice de vectorización, 39,65 %, no deja de ser bajo debido a las instrucciones enteras. Por otro lado, HPCCG sorprende, ya que es el que tiene menor número de bucles, 78, pero pese a ser pocos, se consigue vectorizar un 46 %, que se traducen en un índice de vectorización del 34,94 %. Finalmente, minixyce y CoMD están a la cola como ocurría con sus índices de vectorización respectivos. Sorprende minixyce, ya que tiene más bucles que CoMD, pero su índice de vectorización es insignificante al ser una aplicación mayoritariamente entera Sequoia En la Figura 6.5 (pág. 44), las aplicaciones SPhotmk y Crystalmk, con un 50,5 % y un 46,69 % de instrucciones escalares, respectivamente, parecen claros candidatos a elegir para su análisis. Véase también el desglose de instrucciones en la Tabla 6.3 (pág. 43). En la Figura 6.6 (pág. 44), los motivos incluidos en la categoría other son: unsupported loop structure, cannot vectorize empty loop y conditional assignment to a scalar. Las aplicaciones de este benchmark tienen pocos bucles, comparados a grosso modo con los benchmarks vistos anteriormente. Aquel que más tiene, UMTmk, asciende a 105. Sin embargo,

53 6.4. NPB 43 Figura 6.4: Razones para no vectorizar bucles en Mantevo 1.0 Sequoia Enteras % Escalares % Vectoriales % IRSmk , , ,25 UMTmk , , ,99 Crystalmk , , ,78 SPhotmk , , ,56 Tabla 6.3: Desglose de instrucciones de las aplicaciones de Sequoia dejando de lado este dato, sorprende SPhotmk que, junto con Crystalmk, y pese a tener ambos los peores índices de vectorización, tienen más bucles que la aplicación con mejor índice, IRSmk. Esto significa dos cosas: el número de bucles vectorizados sería mayor proporcionalmente; al haber más bucles, hay más candidatos a ser mejorados y por tanto son aplicaciones interesantes para abordarlas de cara al análisis. Finalmente, la aplicación IRSmk también sorprende porque, teniendo mejor índice, en realidad el número de bucles tratados es inferior a 20. Por tanto, los bucles de que consta, por pocos que sean, concentran la mayor parte del cómputo dando lugar a un rendimiento óptimo pese a la limitación impuesta por las instrucciones enteras NPB La Tabla 6.4 (pág. 45) y la Figura 6.7 (pág. 45) muestran que las aplicaciones UA, IS, BT y LU se corresponderían a los candidatos de más interés. Hasta ahora, en las gráficas mostradas comprobamos que, pese a que una aplicación tenga un buen índice de vectorización, es en las instrucciones escalares donde se encuentran las oportunidades para vectorizar.

54 44 CAPÍTULO 6. CARACTERIZACIÓN DE BENCHMARKS Figura 6.5: Índice de vectorización de las aplicaciones de Sequoia Figura 6.6: Razones para no vectorizar bucles en Sequoia

55 6.4. NPB 45 Figura 6.7: Índice de vectorización de las aplicaciones de NPB NPB Enteras % Escalares % Vectoriales % FT , , ,09 MG , , ,65 EP , , ,39 SP , , ,21 CG , , ,49 LU , , ,42 BT , , ,73 IS , , ,75 UA , , ,55 DC , , ,75 Tabla 6.4: Desglose de instrucciones de las aplicaciones de NPB En la Figura 6.8 (pág. 46), los motivos englobados en la categoría other son: statement cannot be vectorized, condition may protect exception, operator unsuited for vectorization, unsupported data type y cannot vectorize empty loop. La aplicación FT presenta el mejor índice de vectorización, 52,09 %, pero el informe generado por el compilador para ella destaca por el bajo número de bucles tratados, 35. Se trata de otro caso como IRSmk de Sequoia. El índice es bueno porque, al ser pocos bucles, aparentemente se han vectorizado aquellos que se ejecutan más, incluso siendo el porcentaje de bucles vectorizados de solo un 35 %. Por otro lado tenemos UA, con 906 bucles. Si de todos estos bucles un 36 % ha sido vectorizado y un 31 % eran bucles externos, parece que no es suficiente la razón vectorization posible but seems inefficient para justificar el bajo índice de vectorización obtenido, 5,55 %. Parece un caso contrario al de FT, en el que los bucles vectorizados no tienen un peso suficiente en la ejecución. Finalmente, comentar que el compilador ha tratado únicamente 16 bucles para la aplicación EP y, teniendo en cuenta que tiene un índice de vectorización del 33,39 %, demuestra que el cómputo general del programa está repartido entre pocos bucles.

56 46 CAPÍTULO 6. CARACTERIZACIÓN DE BENCHMARKS 6.5. SPEC FP Figura 6.8: Razones para no vectorizar bucles en NPB Pese a que la Figura 6.9 (pág. 47) y la Tabla 6.5 (pág. 47) nos muestran que las aplicaciones cactusadm y zeusmp están por detrás de lbm en el grupo de las aplicaciones con mejor índice de vectorización, el porcentaje de instrucciones escalares no deja de ser menos significativo que otras aplicaciones como namd. Al igual que namd, povray y milc presentan una buena candidatura por el simple hecho de que su índice de vectorización es prácticamente inexistente. En la Figura 6.10 (pág. 48) las razones incluidas en la categoría other son: statement cannot be vectorized, condition may protect exception, operator unsuited for vectorization, unsupported data type y cannot vectorize empty loop. Destaca gamess por la gran cantidad de bucles tratados por el compilador, Sin embargo, pese al 47 % de bucles vectorizados, el índice de vectorización no es más que del 13,08 %, con lo que dichos bucles no tienen gran parte del peso del programa. Por su parte, lbm destaca por tener muy poca cantidad de bucles, 64, pero con más de la mitad de ellos vectorizados ha sido suficiente para obtener un índice de vectorización del 76,41 %. Lo datos presentados dejan fehaciente que, pese a que el compilador esté especialmente diseñado para explotar la vectorización, no se consiguen resultados favorables para todas las aplicaciones. Esto se traduciría en un deficiente uso de la unidad de vectorización. También hay que tener presente el contraste entre el carácter dinámico de la gráfica con el porcentaje de vectorización, frente al carácter estático de aquella con el informe generado por el compilador. Estas razones dan lugar a generar informes más completos del comportamiento de las aplicaciones, para lo cual es necesario completar el simulador CMP$im.

57 6.5. SPEC FP 47 Figura 6.9: Índice de vectorización de las aplicaciones de SPEC fp SPEC fp Enteras % Escalares % Vectoriales % 470.lbm , , , cactusADM , , , zeusmp , , , leslie3d , , , wrf , , , bwaves , , , GemsFDTD , , , gamess , , , tonto , , , gromacs , , , sphinx , , , soplex , , , calculix , , , milc , , , povray , , , namd , , , dealII , , ,71 Tabla 6.5: Desglose de instrucciones de las aplicaciones de SPEC FP

58 48 CAPÍTULO 6. CARACTERIZACIÓN DE BENCHMARKS Figura 6.10: Razones para no vectorizar bucles en SPEC fp

59 Capítulo 7 Adaptación del Simulador En este capítulo se describen las ampliaciones que se han realizado sobre el simulador CMP$impara incluir capacidades de ejecución vectoriales. A la hora de decidir qué tipo de capacidades vectoriales incluir, se ha optado por realizar un modelado sencillo de la arquitectura del coprocesador Intel R Xeon Phi TM. No se pretendía realizar un análisis del rendimiento de este producto, sino tener un modelo que se sabe hace uso de unidades vectoriales con el que generar estadísticas sobre esta característica de formas rápida y sencilla. Para abordar una descripción detallada del simulador construido, se va a partir de las siguientes tres preguntas: Qué características tiene que tener el pipeline para adaptarse a la arquitectura Intel R Xeon Phi TM Coprocessor? Hay datos intermedios ya generados por CMP$imque puedan ser reutilizados? Qué estadísticas particulares permitirán discernir sobre el uso de la unidad vectorial? 7.1. Pipeline Al instrumentar una aplicación, es sencillo contar el número de instrucciones ejecutadas pues únicamente se trata de incrementar un contador. La contabilización de ciclos también sería una tarea sencilla en caso de una máquina multiciclo, en la que hasta que no finaliza una instrucción, no se ejecuta la siguiente. En el caso de una máquina segmentada esta tarea ya no es tan trivial. Hay que tener en cuenta las peculiaridades de la arquitectura para situar a una instrucción en la etapa correcta según avance la ejecución. Rememorando lo comentado en la Sección 5.1 (pág. 31), se presenta la Figura 7.1 (pág. 50) con el boceto sobre dónde se colocaría la simulación del pipeline. Sabemos que se alternan las fases de instrumentación y análisis a medida que se avanza en la ejecución. La fase de simulación de cache se hace durante la fase de análisis usando datos que se han registrado en la fase de instrumentación. La simulación del pipeline es análoga y, en este caso ademas, necesita de la simulación de cache para utilizar la información que de esta se genera. Por tanto, necesariamente tiene que ir a continuación como se visualiza en la figura. El coprocesador Intel R Xeon Phi R es una máquina en orden con terminación fuera de orden (véase Sección 2.3 (pág. 8)). Para simplificar el modelo no se han implementado etapas, con lo 49

60 50 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR Figura 7.1: Pipeline dentro de CMP$im que cuando una instrucción entra en nuestro pipeline, significa que se va a ejecutar, ya sea un acceso a memoria o una división vectorial. Se han tomado las siguientes consideraciones a la hora de implementar el pipeline: Cada instrucción entrará en el pipeline un ciclo después de la entrada de la instrucción anterior. No hay límite de recursos. Los registros se escriben en el banco de registros después de la finalización de la instrucción. Si se producen dependencias de instrucciones no haría falta esperar a la escritura en el banco. Existen buses por donde recoger esa información antes de llegar al banco. La latencia de una instrucción se caracteriza por los ciclos que necesita para completar una operación sin contar las dependencias. Un fallo en el primer nivel de cache que implique traer una línea de los niveles siguientes, congela automáticamente el pipeline tanto si la instrucción siguiente necesita el dato como si no. Esto se produce con cualquier tipo de acceso a memoria bajo demanda (load, store, software prefetch, etc.) Los ciclos situados entre ciclo entrada ins actual ciclo entrada ins anterior + 1 se utilizarán exclusivamente con fines estadísticos. Para ejemplificarlo se utilizará un pequeño kernel escrito en Fortran y basado en el bucle s171 del conjunto de bucles del benchmark LCD. Véase el código en Listado 7.1 (pág. 51). El bloque con mayor número de instrucciones ejecutadas mostrado en el Listado 7.2 (pág. 51) se corresponde exactamente con el bucle do..continue del kernel. Como es un ejemplo simple para mostrar el funcionamiento del pipeline no se ha compilado para la arquitectura AVX-512. Para el ejemplo no se mostrará más que una iteración. La estructura de bloques disponibles es indispensable para lo que sería la creación de un histograma con información estática de las instrucciones. Permitiría entonces acceder a los datos que caracterizan a las instrucciones, como por ejemplo la latencia para aquellos casos donde se realice una operación. Aparte, como se mostró en la Figura 7.1 (pág. 50), para construir un pipeline ficticio es indispensable hacer uso de los datos proporcionados por las rutinas de simulación de cache de CMP$im. En la Tabla 7.1 (pág. 51) se muestra la información recopilada de las instrucciones de más interés desenrolladas por el compilador. Esta información recoge datos de latencias de tlb, cache y operación. La instrucciones que realizan las operaciones de suma y multiplicación entran dentro de la clasificación de las FMA. Su latencia, 4, se puede consultar más adelante en el Listado 7.5

61 7.1. PIPELINE 51 (pág. 55). Véase la Figura 7.2 (pág. 52) con el detalle del pipeline. Téngase en cuenta que la cuadrícula es una aproximación para entender el funcionamiento de nuestro kernel basado en el coprocesador Intel R Xeon Phi R. c c -- Fichero : main. f c integer n real a (100000), b (100000), c (100000) open ( unit =1, file ="N") read (1,*) n close (1) call s171 (a, b, c, n) end c c -- Fichero : s171. f c subroutine s171 (a, b, c, n) integer n real a(n),b(n),c(n) do 10 i = 1, n a(i) = a(i) + b(i) * c(i) 10 continue return end Listado 7.1: Kernel s171 basado en el mismo bucle en LCD f9: movups xmm0, xmmword ptr [ rsi + rax *4] fd: movups xmm1, xmmword ptr [ rsi + rax *4+0 x10 ] : mulps xmm0, xmmword ptr [ rdx + rax *4] : mulps xmm1, xmmword ptr [ rdx + rax *4+0 x10 ] b: addps xmm0, xmmword ptr [ rdi + rax *4] f: addps xmm1, xmmword ptr [ rdi + rax *4+0 x10 ] : movups xmmword ptr [ rdi + rax *4], xmm : movups xmmword ptr [ rdi + rax *4+0 x10 ], xmm d: add rax, 0x : cmp rax, r : jb 0 x4004f9 Listado 7.2: Bloque básico de s171 con mayor número de instrucciones ejecutadas Latencias TLB Cache Operación load load 4 mul 4 4 mul 4 4 add add 4 4 store 4 store 4 Tabla 7.1: Latencias de memoria y de instrucción (load-op) A continuación se explica en detalle el pipeline de la Figura 7.2 (pág. 52):

62 52 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR Figura 7.2: Pipeline del bloque de s171 load - movups xmm0, xmmword ptr [rsi+rax*4]: La latencia es alta porque se ha producido fallo tanto en TLB como en L1. Se ha tenido que llegar a la memoria principal. Como se comentó, esta situación provoca la congelación del pipeline. La siguiente instrucción no entraría hasta el ciclo 726. load - movups xmm1, xmmword ptr [rsi+rax*4+0x10]: cache, DL1, por lo que entra en el 726 y termina en el 730. Acierto en el primer nivel de mul - mulps xmm0, xmmword ptr [rdx+rax*4]: Instrucción tipo load-op. Dependencia del registro xmm0 sobre la primera carga sin consecuencias. La instrucción entraría en el ciclo 726+1, a continuación de la anterior porque el registro xmm0 estaría disponible. Como es load-op carga primero el dato, acierto en DL1 (4 ciclos) y opera (4 ciclos), haciendo un total de 8 ciclos. Entraría pues en el 727 y saldría en el 735. mul - mulps xmm1, xmmword ptr [rdx+rax*4+0x10]: Instrucción tipo load-op. Dependencia del registro xmm1 en el segundo load. Idealmente entraría en el 728, pero como la carga de la que depende no termina hasta el 730, se retrasa 2 ciclos. De nuevo cargaría primero el dato (4 ciclos) y operaría sobre él (4 ciclos). Entraría pues en el ciclo 730 y sale en el 738. add - addps xmm0, xmmword ptr [rdi+rax*4]: Instrucción tipo load-op. Dependencia del registro xmm0 sobre la primera multiplicación. Idealmente entraría en el 731, pero se retrasa 4 ciclos. Falla en el TLB y en la cache y tiene que ir hasta memoria, ocasionando que se congele el pipeline. La siguiente instrucción no entraría hasta que pasaran los 726 ciclos de memoria. Finalmente realiza la suma (4 ciclos). Entra entonces en el ciclo 735 y sale en el add - addps xmm1, xmmword ptr [rdi+rax*4+0x10]: Instrucción tipo load-op. Dependencia del registro xmm1 sobre la segunda multiplicación sin consecuencias. Entra en el ciclo 1461 debido a la congelación del pipeline de la instrucción anterior. Termina en el 1469 por el acceso a memoria (4 ciclos) y la suma (4 ciclos). store - movups xmmword ptr [rdi+rax*4], xmm0: Dependencia del registro xmm0 sobre la primera suma. Idealmente entraría en el ciclo 1462 pero se retrasa 3 ciclos. Entonces entra en el 1465 y termina en el Acierta en la cache.

63 7.2. DETECCIÓN DE INSTRUCCIONES Y REGISTROS 53 store - movups xmmword ptr [rdi+rax*4+0x10], xmm1: Dependencia del registro xmm1 sobre la segunda suma. Idealmente entraría en el 1466 pero se retrasa 3 ciclos hasta el Finaliza en el 1473 pues acierta en el primer nivel de cache Detección de instrucciones y registros La caracterización de las instrucciones es un paso esencial para el funcionamiento de este simulador. Permite conocer al detalle las instrucciones de cara a construir, por ejemplo, una tabla de registros. También permitiría saber cuánto tiempo va a requerir para terminar la operación que contenga, independientemente de las dependencias de los operandos. De cara a las instrucciones exclusivamente de memoria o las load-op, que cargan un dato y operan sobre el mismo, no es posible conocer los ciclos totales hasta la simulación sobre la cache. La API de Pin para instrumentar instrucciones pone a disposición del usuario multitud de funciones que ayudan a la caracterización. Sin embargo, mientras que existen funciones que detectan fácilmente si, por ejemplo, la instrucción es un salto o una comparación, el repertorio no es suficientemente granular para caracterizar instrucciones vectoriales. No es capaz de discernir si la instrucción es simplemente vectorial o si los registros son vectoriales. Lo mismo ocurre con la caracterización de las instrucciones tipo load-op. Hay que tener en cuenta que las instrucciones que se iban a instrumentar eran de las extensiones AVX-512. Estas instrucciones, en el momento de construir el simulador, no eran públicas. Por tanto no era posible para Pin proporcionar una API que permitiera reconocer los nuevos registros de 512 bits. Ya fuera entonces por la no publicidad de la arquitectura, o porque simplemente no disponía de funciones con la suficiente granularidad, se creó el código necesario para subsanar estas carencias Instrucciones INS IsVector: para discernir si una instrucción es vectorial o no, usamos librerías de uso interno en donde todas y cada una de las instrucciones de AVX-512 estaban caracterizadas. Mediante estas funciones era posible consultar campos como la categoría o extensión de una instrucción. Para nuestros propósitos, bastaba con usar la extensión, cuya clasificación era más genérica, reduciendo por tanto las líneas de código. is scalar simd: de nuevo, mediante el uso de la misma librería interna, era posible determinar si una instrucción estaba operando sobre un único o múltiples elementos de un vector. is load op: la heurística consiste en contar el número de operandos de lectura totales de la instrucción, x, y el número de operandos de memoria, y. Si x > y entonces la instrucción es load-op, ya que el operando de más es aquel que se operará junto con el dato cargado de memoria. Véase el detalle en el Listado 7.3 (pág. 53). 1 bool is_load_op ( INS ins ) 2 { 3 if ( IsMemInstruction ( ins )) 4 { 5 UINT32 operandcount = INS_OperandCount ( ins ); 6 UINT32 readop = 0; 7 UINT32 memreadop = 0; 8 9 for ( UINT32 i = 0; i < operandcount ; i ++) 10 if ( INS_OperandRead (ins,i)) 11 { 12 readop ++; 13 if ( INS_OperandIsMemory ( ins, i)) memreadop ++;

64 54 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR 14 } if ( memreadop && readop > memreadop ) 17 return true ; 18 } 19 return false ; 20 } Listado 7.3: Función para detectar instrucciones load-op Registros La detección de registros pasó a formar parte del algoritmo general de instrumentación de instrucciones. También se utilizaron funciones de librerías internas que reconocieran los registros zmm de AVX-512. El modus operandi consistió en analizar individualmente cada operando y clasificarlo del siguiente modo: Operando con registro: es aquel sobre el que se va a operar, ya sea para leer o escribir. Operando de memoria: tuvimos en cuenta que habría registros con los que se opera para obtener una dirección de memoria. La ventaja de acceder a los registros de este modo es que nos permite conocer, a través del operando, si es un registro del que se va a leer o sobre el que se va a escribir. Los operandos de memoria nos permitieron hacer un análisis más granular mediante la clasificación de los registros según el tipo de direccionamiento: base, índice o segmento Latencias En CMP$im, la latencia de las instrucciones se toma por defecto a 1. Para las instrucciones de memoria se agregaría la cantidad de ciclos correspondientes a la latencia, ya suponga un acierto en la L1 o se tuviera que traer la línea desde memoria. Para este trabajo tuvimos que modificar el tratamiento de latencias de las instrucciones alejándolo del sistema ideal por defecto de CMP$im. En el Listado 7.4 (pág. 54) se presenta la clasificación que hicimos de las instrucciones. Por defecto, los valores se mantuvieron a 1. 1 # define NONVPU_LATENCY 1 2 # define MISC_LATENCY 1 3 # define DIV_SQRT_LATENCY 1 4 # define RCP_RSQRT_LATENCY 1 5 # define FMA_LATENCY 1 Listado 7.4: Clasificación de las latencias por defecto Veámoslas en detalle: NONVPU : conjunto de instrucciones enteras. DIV/SQRT : tanto la división como la raíz cuadrada son operaciones muy costosas que tienen semejante número de ciclos.

65 7.3. NUEVAS ESTRUCTURAS Y CLASES 55 RCP/RSQRT : las instrucciones recíprocas tienen equivalente latencia, incluyendo las raíces recíprocas. La categoría recíprocas indica que se está calculando el inverso: 1/x en el caso de las rcp en general y 1/2 x en el caso de las rsqrt. FMA: operaciones de punto flotante que realizan suma y multiplicación en el mismo paso. MISC : resto de instrucciones que no entran en el perfil de las enteras, pero que sí pueden ser vectoriales/escalares. Dado que estas latencias deberían ser modificables, se tomó la decisión de incorporar un knob que permitiera introducir esta configuración a través de un fichero. Véase el ejemplo del Listado 7.5 (pág. 55) 1 # Example configuration file for instruction latencies. 2 # Non - VPU instructions 3 nonvpu # VPU instructions 6 rcprsqrt 8 7 divsqrt 30 8 fma # Default VPU instructions 11 misc 2 Listado 7.5: Fichero de ejemplo con latencias Finalmente, durante la caracterización de instrucciones vista en Sección (pág. 53), se agregó una nueva función denominada GetLatencyByIclass que, haciendo uso de funciones internas, permiten consultar la clase de la instrucción y asignarle una latencia en función de la misma Nuevas estructuras y clases Las estructuras nuevas para el núcleo basado en el coprocesador Intel R Xeon Phi TM que se presenta en este apartado son el resultado de entenderlo como un módulo añadido a toda la simulación de CMP$im. Se necesita la información recogida por las instrucciones, ya sean de memoria o no, y se tiene que respetar el funcionamiento original de CMP$im para ambos modos buffer e instrucción a instrucción. Decidimos por tanto desarrollarlo en dos nuevos ficheros core.cpp y core.h. La localización de las llamadas al pipeline de este núcleo era un problema que había que abordar desde el punto de vista del análisis más que de instrumentación. Recordamos el análisis realizado sobre el pipeline en la Sección 7.1 (pág. 49). Decidimos situarlas antes de la ejecución de cada bloque. Véase la Figura 7.3 (pág. 56). Este esquema encaja con el modo buffer porque previamente se han simulado las instrucciones del bloque anterior. En el caso del primer bloque no se simulará nada, mientras que en el caso del último bloque será la función de finalización la que lanzaría el pipeline. También encaja con el modo instrucción a instrucción porque cuando se ha llegado al bloque siguiente, las instrucciones de memoria del anterior ya se han simulado en la cache, aunque fuera individualmente. La idea consistió en desarrollar una clase nueva denominada CORE. Este concepto de core es ligeramente distinto a lo que veníamos nombrando como núcleo. La clase CORE representa el esquema de los cores que componen el coprocesador. Si quisiéramos simular la tarjeta completa, se crearían 50 objetos. Dado que en este trabajo no se toca el campo multi-thread, con simular un core era suficiente. La idea se asemeja a la clase CACHE explicada en la Sección 5.2 (pág. 33). De

66 56 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR Figura 7.3: Localización del simulador de pipeline en CMP$im hecho, están íntimamente relacionados, ya que cada objeto CORE creado tendrá su objeto CACHE asociado. Cada objeto CORE además tendría su propio pipeline y sus propias estructuras de almacenamiento de estadísticas y del estado del core en cada momento. Por otro lado, al tener que almacenar información detallada sobre todas las instrucciones instrumentadas, ya fueran de memoria o no, era necesaria una estructura solo modificable en tiempo de instrumentación con el footprint de la aplicación. Además, también sería necesario guardar los registros utilizados en tiempo de análisis para detectar dependencias. Véase la Figura 7.4 (pág. 56) para obtener una imagen visual de estos conceptos. Figura 7.4: Idea para la implementación de KNC Las estructuras y clases principales desarrolladas para ello fueron las siguientes: Footprint Basic Block State Register File State Stats Core

67 7.3. NUEVAS ESTRUCTURAS Y CLASES 57 FOOTPRINT Durante la fase de instrumentación se analizan todas las instrucciones de la aplicación. Es necesario por tanto almacenar la información estática recopilada de cada una de ellas para su posterior uso durante la fase de análisis. Esta estructura se implementó para tal fin. Los campos de que consta son los siguientes: Tipo: enumerado que recoge la siguiente clasificación posible: 1 typedef enum 2 { 3 INS_TYPE_NONVPU, 4 INS_TYPE_V_VECTOR, 5 INS_TYPE_V_SCALAR, 6 INS_TYPE_MEM, 7 INS_TYPE_NUM 8 } INS_TYPE_t ; Entera Escalar Vectorial Memoria Latencia operacional: si una instrucción tiene esperar a que otra termine de realizar una operación para usar su resultado, es necesario conocer cuánto va a tardar la operación en sí. Esto incluye a las instrucciones denominadas load-op, las cuales a la vez que cargan un dato en la cache, operan sobre él una vez disponible. Sin embargo, este campo no servirá para almacenar la latencia provocada por una instrucción de memoria. Tamaño de la instrucción: para saber exactamente en qué punto termina un bloque básico. Registros fuente y destino: necesarios para realizar el control de dependencias de registro. Desensamblado: con fines de depuración. Rutina: a qué rutina pertenece la instrucción. Esta estructura se declaró sobre un mapa STL, siendo la clave de acceso el PC de la instrucción. Véase el Listado 7.6 (pág. 57). 1 typedef map < UINT64, INS_FOOT_PRINT_t > FOOT_PRINT ; Listado 7.6: Footprint de las instrucciones de la aplicación REGISTER TABLE La detección de las dependencias entre registros requiere llevar un seguimiento de las escrituras de los mismos. Por ello, se creó una pequeña estructura que almacena la siguiente información. Ciclo: se actualiza cada vez que se escriba el registro. Por tanto, indica el ciclo en el que un registro estará disponible. Dirección: PC de la instrucción que ha sido la última en escribir dicho registro. Sirve para poder responsabilizar a una instrucción de que otra ha tenido que atrasar su ejecución. Desglose de ciclos: para casos en que una instrucción de memoria haya escrito el registro, se almacena el desglose de ciclos generados para conseguir el dato. Esta información es temporal y solo influye en tiempo de simulación, no en los resultados finales. Serán vectores de tamaño fijo dado por las macros KNC TLB LVLS y KNC CACHE LVLS.

68 58 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR Figura 7.5: Instrucción que toca dos líneas Esta estructura se declaró en el interior de un mapa STL indexado por el código del registro. Habrá tantas entradas como registros se hayan accedido en la ejecución. Véase el Listado 7.7 (pág. 58). Esta tabla solo se actualiza durante la simulación, puesto que ya tenemos disponibles los registros utilizados por cada instrucción. Estos fueron previamente almacenados durante la instrumentación de las instrucciones. 1 typedef map < UINT32, REG_FILE_STATE_t > REG_FILE ; Listado 7.7: Mapa STL de la Tabla de Registros BASIC BLOCK STATE Para llevar un seguimiento del comportamiento de las instrucciones de memoria para cada uno de los bloques básicos ejecutados por la aplicación, presentamos la siguiente estructura. Tiene carácter temporal. Los campos definidos son los siguientes: Nivel de hit de TLB: es el último nivel de TLB al que se ha accedido. Este nivel incluiría la memoria principal como último nivel en caso de tener que acceder a la tabla de páginas del proceso. Nivel de hit de Cache: análogo al campo anterior, pero para el caso de la jerarquía de cache. Desglose de ciclos de la TLB y de la CACHE: aquí se van almacenando los ciclos requeridos por una instrucción al acceder a distintos niveles de ambas caches. Serán vectores de tamaño fijo dado por las macros KNC TLB LVLS y KNC CACHE LVLS. Fue declarada como parte de un mapa STL indexado por una clave que tiene dos campos <PC, acceso>. Véase el Listado 7.8 (pág. 59) con la declaración con los tipos de datos requeridos. Al realizar una instrucción un acceso a memoria, existe la posibilidad de que tenga que realizar más de un acceso. Es posible que el dato no esté alineado, teniendo que tocar dos líneas de cache. Véase la Figura 7.5 (pág. 58). En estos casos es necesario almacenar los accesos por separado ya que el primero puede ser un fallo y el segundo un acierto. Si este histograma se indexara solo por el PC de la instrucción, destruiríamos la información del primer acceso al registrar el segundo. Esto también ocurre de forma totalmente distinta, en el caso de instrucciones tipo gather. Esta instrucción tiene la especialidad que no toca dos líneas porque el dato no está alineado, el interés radica en que se despliega en todo un conjunto de accesos independientes los cuales a su vez pueden estar desalineados. Al acceder a la estructura con el par <PC, acceso> el problema desaparece. En el mapa de ciclos el primer acceso tendrá el valor <PC,0>, y el resto de accesos, por numerosos que fueran, se indexarían incrementando el segundo campo.

69 7.3. NUEVAS ESTRUCTURAS Y CLASES 59 1 typedef std :: pair < UINT64, UINT32 > BBL_ENTRY_KEY ; 2 typedef map < BBL_ENTRY_KEY, BBL_STATE_t > BBL_STATE ; Listado 7.8: Mapa STL de los accesos a cache de las instrucciones de memoria de un BBL STATE Para hacer una fotografía al estado actual de la simulación se creó esta estructura. Engloba campos creados a partir de las estructuras anteriores BBL ST AT E y REG F ILE. Además almacenará los siguientes campos: Ciclo de issue: en el momento de la simulación, es importante conocer el ciclo en el que se inserta una instrucción en el pipeline. De este modo se podrá determinar cuándo empezaría la siguiente. Ciclo de write back: en el caso de instrucciones que escriban en un registro, ya sea el resultado de una operación o una carga de memoria, es importante saber en qué ciclo escribe de cara a entregarle ese dato a la tabla de registros. Estado de la memoria: desglose de ciclos de la última instrucción de memoria ejecutada. Se creó para abrir la posibilidad de lanzar una aplicación multi-thread. Última instrucción: información sobre la última instrucción que ocupó el pipeline. STATS La estadísticas son una parte fundamental. En esta clase se acumularían los datos necesarios para su tratamiento a posteriori. En la Sección 7.4 (pág. 62) se explica al detalle este posttratamiento. Las estadísticas se acumulan desde dos perspectivas: a nivel de instrucción y a nivel global. Los campos de que consta las estadísticas por instrucción son los siguientes: Bytes cargados: para propósitos de validación. Si sabemos exactamente cuántos elementos va a recorrer un bucle y de qué tipo son, podemos localizar el bloque del bucle en el informe final y validar que sus instrucciones de memoria han cargado el número de bytes esperados. Ciclos totales de parada: número acumulado de ciclos que una instrucción ha tenido que esperar, o ha tenido que hacer esperar, para entrar en el pipeline. Estas dos opciones se corresponden con dos modos: productor y consumidor. Se trata de decidir a qué instrucción culpar del retraso experimentado por una instrucción. Véase Sección 7.4 (pág. 62) para más detalles. Desglose de ciclos de parada (stalls): cada vez que una instrucción tiene que entrar más tarde al pipeline, ocasiona un incremento en el cómputo total de ciclos del programa impidiendo un IPC de 1. De cara a conocer qué recursos son los que están trabajando más para generar el dato motivo de la dependencia, es necesario almacenarlo. 1 typedef enum 2 { 3 INS_STALL_ISSUE, 4 INS_STALL_NONVPU, 5 INS_STALL_V_SCALAR, 6 INS_STALL_V_VECTOR,

70 60 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR 7 INS_STALL_NUM 8 } INS_STALL_t ; Listado 7.9: Tipos de stalls A los tipos de stalls presentados en el Listado 7.9 (pág. 59) se tendrían que añadir aquellos relacionados con los accesos a memoria. Sin embargo las paradas por memoria se almacenarán en vectores individuales que no necesitan el uso de un enumerado. Léase la descripción de todas las opciones a continuación: issue: ciclos correspondientes a las instrucciones que lograron entrar al pipeline exactamente un ciclo después que la anterior. Es un motivo optimista. nonvpu: ciclos que las instrucciones tuvieron que esperar para entrar al pipeline a causa de dependencias con instrucciones que no son ni escalares ni vectoriales. vpu 1 y vpu N : ciclos a esperar a causa de dependencias con instrucciones escalares y vectoriales respectivamente. Desglose de ciclos de parada por memoria: en el caso de que el motivo de la parada fuera la memoria, se almacenará en vectores separados para el TLB y para el resto de la jerarquía de cache. Léase descripción detallada: tlb: ciclos a esperar a causa de la traducción de direcciones. Este campo estaría a su vez desglosado según la jerarquía establecida para los tlbs. cache: ciclos que se ha tenido que esperar en los niveles de cache que se hayan configurado, incluyendo la memoria principal, a causa tanto de dependencias de datos de memoria como por congelaciones ocurridas en el pipeline debido a fallos en el primer nivel de cache. Estos se tienen que resolver antes de que las siguientes instrucciones se sigan ejecutando. Las estadísticas por instrucción fueron definidas en un mapa cuyo índice es el PC de la instrucción. 1 typedef map < UINT64, STATS_INS_s > STATS_INS_t ; Listado 7.10: Mapa STL de las estadísticas por instrucción Los campos de que consta las estadísticas globales son los siguientes: Sumatorio global de ciclos: número global de ciclos consumidos en el core por la aplicación. Desglose global de ciclos de parada (stalls y memoria): como en el caso del desglose por instrucción, en este caso son los datos globales del core. Al final se creó una estructura sencilla ambas estadísticas: 1 typedef struct 2 { 3 STATS_INS_t * stats_ins ; 4 STATS_GLB_t * stats_glb ; 5 } STATS ; Listado 7.11: Estadísticas por instrucción y globales

71 7.3. NUEVAS ESTRUCTURAS Y CLASES 61 CORE CORE es una clase que englobará todas las estructuras anteriores excepto el Footprint, puesto que ésta es una estructura global para la aplicación y no específica del core. El resto sí forman parte de la especificación detallada de un CORE del coprocesador Intel R Xeon Phi TM. La visión simplificada de la misma es la siguiente: 1 class CORE { 2 3 STATE state ; 4 STATS stats ; 5 6 UINT32 coreid ; 7 8 void InsertInPipeline ( 9 UINT32 tid, 10 BBL_STATE :: iterator ins ); void DistributeCycles ( 13 UINT32 tid, 14 UINT64 storelip, 15 COUNTER cycles, 16 BBL_ENTRY_KEY culprit, 17 bool regstall, 18 bool memstall, 19 BBL_ENTRY_KEY currentip = make_pair (0,0), 20 INT32 regdependency = -1); void InsertBreakdownStats ( 23 UINT32 tid, 24 STATS_INS_t :: iterator, 25 UINT32 cycles, 26 BREAKDOWN_t breakdown, 27 UINT32 index ); public : CORE ( UINT32 coreid, UINT32 nthreads ); ~ CORE (); void Pipeline ( UINT32 tid, BBInfo * bbl ); string PrintGlobalStats ( UINT32 tid ); } Listado 7.12: Clase CORE Estas son las funciones destacadas: Pipeline: pública. Se describió a grosso modo en la Sección 7.1 (pág. 49). Una vez terminada la simulación de la cache, es la función encargada de organizar el pipeline para las instrucciones del bloque ya simulado en cuestión. InsertInPipeline: privada. Inserta una única instrucción en el pipeline. Será invocada desde la función Pipeline. DistributeCycles: privada. Una vez insertada una instrucción en el pipeline, se invocará para distribuir los ciclos que ha tardado en entrar al pipeline, ya hubiera habido stalls o no. InsertBreakdownStats: privada. Función genérica para determinar sobre qué vector se va a insertar el desglose de ciclos generado por una instrucción.

72 62 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR Finalmente se creó un array de punteros a objetos de la clase CORE sobre el que se insertarían todos aquellos que se establecieran en la configuración. 1 extern CORE * CoreArray [ MAX_EXPERIMENTS ][ MAX_NUM_THREADS ]; Listado 7.13: Array de punteros a objetos CORE 7.4. Estadísticas Durante el procedimiento de construcción del pipeline, no solo se tendrá información del número total de ciclos, sino que para cada bloque tratado también se dispondrá específicamente las razones por las que las instrucciones tuvieron que entrar más tarde al pipeline. En el primer caso había que tener en cuenta que el último ciclo de la aplicación no tiene por qué ser el ciclo de fin de la última instrucción del programa. El motivo es la terminación en desorden. Respecto a la segunda información, es necesario recogerla para poder saber qué recursos son aquellos que impiden al programa ir más rápido. Una parte de la información sobre las instrucciones se encuentra insertada en el FOOTPRINT y otra en la rama de la estructura STATS dedicada a instrucciones exclusivamente. Ambas han sido ya expuestas en la Sección 7.3 (pág. 55). A la hora de almacenar los ciclos que ocasionan stalls se planteó la posibilidad de decidir sobre qué instrucción almacenarlos. Se trataba de determinar a cual culpabilizar. De cara a visualizar el cómputo total de ciclos esta decisión es indiferente, pero no lo es si quisiéramos consultar información de un bloque concreto. Las dos alternativas sobre las que responsabilizar son: Productor: si una instrucción depende de que otra genere un dato, será la productora sobre la que recaerían los ciclos. Consumidor: en este caso, culparíamos a la instrucción dependiente por tener que necesitar un dato. La opción por defecto fue responsabilizar al consumidor. Finalizada por tanto la ejecución de la aplicación, y la recogida de las estadísticas correspondientes, es el turno de la función que realmente pone fin a toda la simulación. Si se recuerda la Sección 5.1 (pág. 31), en caso de que la ejecución fuera en modo buffer, al no haber más bloques que se encarguen de lanzar la última simulación de cache, es necesario que esta función se haga cargo de subsanarlo. Posteriormente, tanto para modo buffer como instrucción a instrucción, se lanzaría la construcción del pipeline y se recogerían las estadísticas. Finalmente, llegaría el momento de invocar a las funciones que se encargan de escribir toda la información recopilada en un fichero para su posterior tratamiento. Ni qué decir tiene que CMP$im ya almacenaba información acerca de los accesos y fallos de las instrucciones desglosadas por nivel de cache, entre otros. Esta información será aprovechada para completar nuestras estadísticas. El objetivo reside en crear una tabla en el informe de salida con toda la información desglosada por cada una de las regiones tratadas: funciones, bloques e instrucciones; y separadas por comas,,. La cabecera contendría los siguientes campos: Región: cada una de las entradas estaría clasificada con las palabras clave FUN, BEG, INS y GLB, significando función, bloque, instrucciones y global, respectivamente. La global será una región mostrada al final de la tabla con el acumulado de todos los datos.

73 7.4. ESTADÍSTICAS 63 Direcciones de comienzo y fin: en el caso de funciones y bloques, serían las direcciones de comienzo y fin. La dirección de comienzo coincide con el PC de la primera instrucción, y la dirección de fin coincide con la primera instrucción del bloque siguiente. El objetivo es que esta información incluya el tamaño de estas regiones sin tener que agregar más campos. En el caso de las instrucciones, aparecería únicamente su PC, quedando el campo de dirección de fin vacío. Ejecuciones del bloque: solo se mostrará si la región es el bloque, quedando vacío para las regiones restantes. Indica el número de veces que se ha ejecutado el bloque. Número de instrucciones: para funciones y bloques, contendrá el número total de instrucciones ejecutadas. Número total de bytes: se corresponde con el total de bytes que han sido solicitados por las instrucciones de carga, load. Su finalidad es, entre otras, de validación, para comprobar que efectivamente el número de bytes cargados por un bloque particular es el esperado. Se mostrará para todas las regiones. Número total de ciclos: es un modo de tener una idea del número total de ciclos requeridos por todas las instrucciones sin necesidad de realizar cálculos adicionales. Accesos desglosados: esta información se encontraba ya disponible para cada instrucción, desglosada por nivel de cache. Se presentará pues para cada instrucción tal y como está ya, y se realizará el acumulado para mostrarlo por bloque y función. Fallos desglosados: es análogo a los accesos, pero mostrando los fallos. Ciclos desglosados: se corresponden con toda la retahíla de razones explicadas al inicio de esta sección. En este punto, lo tenemos todo para visualizar la información. Sin embargo, si recordamos el punto sobre los bloques básicos de la Sección 5.2 (pág. 33), originalmente se almacenaban en una lista para no ralentizar la simulación. Es ahora cuando se tiene que trabajar sobre la lista para convertirla en un mapa con bloques únicos. Inicialmente, se lleva a cabo generando una worklist indexada por una clave de dos campos, <PC,PC>, correspondientes a la dirección de la primera instrucción y de la última. Véase la declaración en el Listado 7.14 (pág. 63). A continuación, se analizan los bloques de dos en dos, como se muestra en la Figura 7.6 (pág. 63) y la Figura 7.7 (pág. 64), para cortarlos y fusionarlos siempre que sea necesario. Una vez hecho esto, el proceso consiste en un bucle que se recorre mientras haya entradas (bloques) en la worklist. Para evitar problemas al tratar el último bloque, se introduce al final de la worklist uno ficticio con unas claves ficticias. A medida que se recorre la worklist, en caso de no haber ningún corte se elimina el primer bloque para introducirlo en el mapa de bloques únicos. Si se produce un corte, se modifican los bloques consecuentemente y se vuelve a iterar. Cuando la lista solo contiene el bloque ficticio se finaliza el proceso. 1 map <pair < UINT64, UINT64 >, COUNTER > BBInfoMap ; Listado 7.14: Mapa STL de Bloques Básicos Figura 7.6: Bloques con ningún y un corte Finalmente, se escribiría toda la información en el informe final y se finalizaría el proceso.

74 64 CAPÍTULO 7. ADAPTACIÓN DEL SIMULADOR Figura 7.7: Bloques con 2 cortes 7.5. Invocación activando la funcionalidad vectorial La línea de invocación que veíamos en la Sección 5.3 (pág. 37) se incrementa en un knob con el que se activa la funcionalidad vectorial. La entrada de datos del knob nuevo es el fichero de configuración de las latencias de instrucciones que se presentaron en la Sección (pág. 54). 1 pin -t cmpsim - cache DL1 :32:64:8:1 2 - cache UL2 :1024:64:16:1 3 - tlb DTLB :64:4096:8 4 - tlb DTLB2 :256:4096:8 5 - dl1lat 4 6 -l2lat dtlblat dtlb2lat inclusive threads dependencies inslat. conf -- <app > <input > Listado 7.15: Línea para el lanzamiento de la simulación

75 Capítulo 8 Estudio experimental En el Capítulo 6 (pág. 39) presentamos la caracterización inicial de los benchmarks seleccionados, desde los puntos de vista del indice de vectorización y el desglose de motivos determinados por el compilador para no vectorizar. Teniendo esto en cuenta este capítulo fue dividido en dos partes: resultados y diagnóstico. En la primera presentaremos gráficamente los resultados obtenidos a partir de la simulación en el CMP$im modificado con las capacidades vectoriales del Intel R Xeon Phi TM descrito en el Capítulo 7 (pág. 49). Estos resultados incluyen las ejecuciones escalares y vectoriales de todas las aplicaciones. Queríamos conocer, tanto a nivel de instrucciones como de ciclos, cuán diferente es la versión vectorizada frente a aquella en la que no se activó la vectorización. El motivo reside en que esta comparativa proporciona un punto de vista adicional al ya visto con los índices de vectorización. Con esta información decidiremos entonces acerca de qué aplicaciones analizar en profundidad para detectar los focos donde se pueda explotar la vectorización en mayor medida. En la segunda parte del capítulo, el diagnóstico, dividiremos el análisis en dos partes: software y hardware. En el diagnóstico software se analizan los bloques básicos más ejecutados de la aplicación, el código fuente y el compilado. En el diagnóstico hardware se presentarán los resultados obtenidos al modificar la cache, de cara a comprobar qué efectos tendría en aquellas aplicaciones que, pese a tener un buen índice de vectorización, se encuentran limitadas en su ejecución por los accesos a memoria Resultados Polyhedron La Figura 8.1 (pág. 66) muestra la relación entre el número de instrucciones y de ciclos de las versiones vectorizada y no vectorizada de los benchmarks de Polyhedron. El eje de abscisas se corresponde con la relación de instrucciones ejecutadas de la versión vectorizada frente a la no vectorizada. El eje de ordenadas se corresponde con la relación de ciclos, obtenidos a través de la versión modificada de CMP$im, también entre las versiones vectorizada y no vectorizada. Además, se han dibujado segmentos de los siguientes tres colores: verde: marca los puntos de la gráfica en los que la relación de instrucciones para ambas versiones es igual a 1. rojo: análogamente al segmento verde, marca los puntos de la gráfica en los que la relación de ciclos para ambas versiones es igual a 1. 65

76 66 CAPÍTULO 8. ESTUDIO EXPERIMENTAL morado: indica los puntos donde las relaciones de instrucciones y ciclos sen han visto afectadas de forma equitativa. A continuación se explican en detalle las diferentes situaciones e implicaciones de cada punto en esta gráfica desde los puntos de vista de las instrucciones y los ciclos: Instrucciones == 1: linea verde. Se pueden dar dos circunstancias: o bien no se ha vectorizado, o bien sí se ha hecho pero la inclusión de instrucciones adicionales relacionadas con la vectorización ha hecho que el número total de instrucciones no disminuya. Es común ver a las aplicaciones que cumplen esto arremolinadas en el punto de intersección (1, 1). < 1: se ha conseguido vectorizar. Se encontrarían dentro del área cuadrada encerrada por los segmentos verde y rojo. > 1: se ha vectorizado pero, debido a comprobaciones o instrucciones relacionadas con la vectorización, el número de instrucciones ejecutadas ha resultado ser mayor. En la gráfica, estas aplicaciones estarían fuera del área cuadrada formada por los segmentos verde y rojo. Figura 8.1: Versión vectorizada vs no vectorizada de Polyhedron Ciclos == 1: linea roja. Hay dos circunstancias en relación a la relación de instrucciones vista: 1. relación de instrucciones == 1: si no hay mejora de instrucciones no la hay de ciclos. 2. relación de instrucciones < 1: pese al éxito de la vectorización, la aplicación está limitada por memoria. Las aplicaciones se encontrarían adyacentes al segmento rojo. < 1: de nuevo se producen dos circunstancias: 1. la reducción de instrucciones y de ciclos siguen más o menos la misma línea. Aquellas próximas al segmento morado cumplen este patrón.

77 8.1. RESULTADOS pese a que se hayan reducido el número de instrucciones, esta reducción no se ha traducido en una reducción de ciclos equivalente. Se encontrarían dentro del área encerrada por los segmentos morado y verde. > 1: la vectorización provocó un aumento de ciclos. Volviendo a Polyhedron, la caracterización de las aplicaciones tfft, fatigue, doduct, mdbx, nf e induct, las hacía destacar como posibles candidatas para su análisis dado alto porcentaje de instrucciones escalares frente a las vectoriales. Entre ellas, la Figura 8.1 (pág. 66) nos destaca fatigue y, como novedad, gas dyn. La primera porque al vectorizar se ha obtenido un 3 % más de instrucciones que han supuesto un 9 % más de ciclos. La segunda porque en su caracterización pasaba desapercibida entre el resto, mientras que aquí destaca frente a la versión escalar, con un 90 % menos de instrucciones ejecutadas y un 86 % menos de ciclos. Por otro lado, aplicaciones como channel, linpk y test fpu, que eran las que mejor índice de vectorización mostraban en su caracterización, en esta gráfica se muestran como un claro ejemplo de estar limitadas por la memoria. Se observa que la reducción de instrucciones no se traduce en la misma medida en la de ciclos. En el caso de channel, por ejemplo, es un 79 % menos de instrucciones frente a un 9 % menos de ciclos. El resto de aplicaciones se encuentran o bien distribuidas a lo largo del segmento morado, el cual es un buen resultado ya que indica que la vectorización ha funcionado, o bien aglomeradas en el punto (1, 1), donde la vectorización no ha dado buenos resultados. Figura 8.2: Ciclos desglosados de las aplicaciones de Polyhedron La Figura 8.2 (pág. 67), que muestra en forma de ciclos las razones por las que los IPCs de las aplicaciones son inferiores a 1, sirve para confirmar que las aplicaciones channel, linpk y test fpu, están claramente penalizadas por los continuos accesos a la memoria principal. Las denominaremos memory bound. tfft, pese a que en su caracterización presentaba el peor índice de vectorización, 0,07 %, resulta que también es memory bound, con lo que una posible mejora en la vectorización podría no traducirse en una mejora de ciclos. Las mejoras para abordar esta aplicación residirían en encontrar una configuración de memoria adaptada a ella, para luego identificar las deficiencias en su código. En el caso de capacita, que también entra en esta categoría, es la única que presenta un perfil semejante, ya que tiene un 38 % de ciclos a consecuencia de fallos en el segundo nivel de TLB. En este caso, parece viable probar a ejecutarla modificando el número de líneas en el TLB de segundo nivel. Siguiendo la línea de las aplicaciones memory bound, tenemos a gas dyn como caso particular. Pese a su excelente resultado respecto a la versión escalar, su IPC es 0,19. Esto se debe a los fallos

78 68 CAPÍTULO 8. ESTUDIO EXPERIMENTAL en DL1, que se traducen en un porcentaje alto de aciertos en UL2. Por muy buen uso de la unidad vectorial que haga, el rendimiento general de la aplicación no es acorde. Ya que los responsables son los fallos en la DL1 y no en la UL2, sería interesante de analizar. Al lado derecho de la gráfica tenemos aplicaciones como induct, doduc, aermod y fatigue, que tienen los mayores porcentajes de ciclos ocasionados por dependencias sobre instrucciones escalares. Si se aumentara el número de vectoriales que sustituyeran a las escalares, se reducirían estos ciclos. Es posible que, como efecto colateral, se trasladaran las dependencias entre escalares a las vectoriales. Sería una solución aceptable pues habría mejorado el uso efectivo de la unidad vectorial del procesador, pudiéndose plantear soluciones de otra índole. De todos modos, hay que recordar que el compilador conseguía vectorizar un 67 % de bucles de doduc, por lo que no da mucho margen de maniobra. No es así con induct, fatigue y aermod, que se situaban a la cola. La de más interés parece ser aermod al tener mas de 2500 bucles registrados. Finalmente tenemos a protein, aplicación con un 96 % de instrucciones enteras. Fue descartada de entrada como candidata a ser analizada debido a que los esfuerzos deberían centrarse en la reducción de instrucciones escalares. Como las enteras tienen una latencia de un ciclo por defecto, provocan que el porcentaje de instrucciones que entran correctamente en el pipeline aumente, incrementando entonces el IPC y situándola a la cabeza. Recuérdese que si una instrucción entraba correctamente en el pipeline, el ciclo se clasificaba con el nombre Issue. Sin embargo, dejando a un lado este detalle, el límite para que se obtenga un resultado mejor son los accesos a DL1. En realidad estos accesos son en su mayoría aciertos en la DL1, ya que no se ve la participación ni de la UL2 ni de memoria principal. Por tanto, estos ciclos implican dependencias de otras instrucciones sobre los datos cargados por loads. Representan un 47 % del total de ciclos. Este tipo de aplicaciones sería mejor englobarla fuera de las memory bound para clasificarlas en su lugar como cache dependence bound. Las candidatas a un análisis más profundo, según los razonamientos arriba expuestos, son: aermod: su objetivo es simular el modelo de dispersión de aire ISCST2. induct: su propósito es generar la entrada de PSpice que sirva para el modelado de las propiedades electromagnéticas de baja frecuencia de un sistema de comunicaciones res-q. fatigue: sirve para modelar la fatiga de los metales dúctiles. gas dyn: destinada a resolver ecuaciones de continuidad de masa, momento y energía con el objetivo de modelar el flujo de un gas es una dimensión Mantevo La caracterización de las aplicaciones de Mantevo invitaba a examinar algunas de ellas como Cloverleaf, ya que su índice de vectorización ascendía a un 73 % de las instrucciones ejecutadas del programa. El resto de aplicaciones con un índice inferior al 40 % y con poco peso en cuanto a instrucciones escalares, no parecían ser candidatas de interés. Entre ellas, la que obtenía peores resultados era CoMD, con un 0,21 % de instrucciones vectoriales frente a un 30 % de escalares, datos que parecían concordar con el informe del compilador. La Figura 8.3 (pág. 69), que se presenta con la comparación entre las versiones escalar y vectorial, muestra un Cloverleaf que presumiblemente va a estar limitado por la memoria principal. La reducción de ciclos es de un 19 % frente al 81 % sobre las instrucciones. Este mismo comportamiento lo presentan HPCCG, minife y minighost. CoMD se reitera como candidata a ser analizada porque se encuentra fuera del área encerrada por los segmentos.

79 8.1. RESULTADOS 69 Figura 8.3: Versión vectorizada vs no vectorizada de Mantevo El desglose de ciclos de la Figura 8.4 (pág. 69) confirma que Cloverleaf, HPCCG, minife y minighost se engloban dentro del grupo memory bound. Por otro lado, pese a que en CoMD se da la circunstancia mencionada sobre que se encuentra fuera del área en la primera figura, significando que la versión vectorial ha sido peor que la escalar, presenta aquí un interesante porcentaje de dependencias con instrucciones escalares sobre las que se puede centrar el análisis. Esto reafirma una vez más su candidatura. Figura 8.4: Ciclos desglosados de las aplicaciones de Mantevo Finalmente vemos que minixyce es una aplicación con un comportamiento análogo al manifestado por protein de Polyhedron. Su caracterización mostraba un 69 % de instrucciones enteras de latencia 1, que se traducen aquí en un gran porcentaje de ciclos Issue que incrementan el IPC falseando su apariencia de buena candidata. La única candidata de este benchmark para ser analizada en profundidad, será: CoMD: es un proxy para computación en aplicaciones tipo Molecular dynamics. Todas las aplicaciones de este tipo tienen características compartidas, como son los parámetros x, y, z

80 70 CAPÍTULO 8. ESTUDIO EXPERIMENTAL de cada partícula, operaciones en las que unas partículas interaccionan con otras, etc Sequoia Sequoia presentaba ya dos candidaturas para realizar un diagnóstico software: SPhotmk y Crystalmkm. En la Figura 8.5 (pág. 70) Crystalmk presenta un comportamiento a ser analizado por su empeoramiento respecto a la versión escalar, que asciende a un 13 % de empeoramiento en cuanto a instrucciones y un 15 % en ciclos. IRSmk y UMTmk presumiblemente estarán limitadas por memoria, pese a tener unos índices de vectorización del 54 % y 24 %, respectivamente. Este hecho se ve corroborado en la Figura 8.6 (pág. 70). Figura 8.5: Versión vectorizada vs no vectorizada de Sequoia Figura 8.6: Ciclos desglosados de las aplicaciones de Sequoia En el caso de SPhotmk y Crystalmk presentan altos porcentajes de dependencias ocurridas por instrucciones escalares, por tanto siguen siendo candidatas: SPhotmk: consiste en un conjunto seleccionado de pequeñas porciones de código de otros

81 8.1. RESULTADOS 71 paquetes mayores. No realiza cálculos físicos per se, sino que engloba procesos como un solucionador de sistemas lineales por Cholesky, bucles con divisiones, bucles con llamadas a funciones matemáticas de tipo built.in, etc. SPhotmk: sirve para medir el rendimiento de la CPU y comprobar que los resultados que proporciona son correctos NPB Las aplicaciones que inicialmente se consideraron candidatas fueron UA, IS, BT y LU. En éstas, el grado de instrucciones escalares era del 68, 35, 71 y 50 %, respectivamente. El hecho de que el número de instrucciones escalares sea grande ayuda a que, localizando los bloques a los que pertenecen, se puedan concentrar los esfuerzos en ayudar al compilador a vectorizarlos. Figura 8.7: Versión vectorizada vs no vectorizada de NPB En el área delimitada por los segmentos rojo y morado de la Figura 8.7 (pág. 71), se encuentran las aplicaciones limitadas por la memoria principal, más próximas al segmento rojo que al morado. En este benchmark, a diferencia de los anteriores, nos encontramos con muy pocas aplicaciones en el punto (1,1), donde figurarían aquellas cuyos resultados de la versión vectorial se asemejarían a la versión escalar. En cambio, la mayoría se encuentra limitada por memoria en mayor o menor medida. Entre las destacadas por la limitación de memoria se encuentran MG, con un 78 % de reducción de instrucciones frente al 26 % de mejora en ciclos; FT, con un 63 % de reducción frente al 29 % de ciclos, SP con un 55 % y 16 %, respectivamente, y CG con un 51 % de instrucciones reducidas frente al 5 % de ciclos. De las mencionadas, FT fue la que presentaba el mejor índice de vectorización, pese a ser memory bound, con un 52 % de instrucciones vectoriales ejecutadas frente al 2 % de escalares. EP, por el contrario, tenía un índice de vectorización que competía con el de instrucciones escalares (33 % y 30 %, respectivamente). Sin embargo, aquí es la que presenta una mayor mejora, teniendo un 65 % de reducción de instrucciones y 53 % de ciclos. En el otro extremo tenemos DC. Su índice de vectorización mostraba un 97 % de instrucciones enteras ejecutadas. Aquí figura como que el intento de vectorización no ha hecho sino empeorar el número de instrucciones en un 2 %. La relación de ciclos se ha mantenido constante, presumiblemente por el gran número de instrucciones enteras. Por este motivo, no merece la pena pararse a

82 72 CAPÍTULO 8. ESTUDIO EXPERIMENTAL analizar las razones de esta situación, cuando en general no vamos a poder aumentar el número de instrucciones vectoriales de la misma. Figura 8.8: Ciclos desglosados de las aplicaciones de NPB En la Figura 8.8 (pág. 72) los ciclos desglosados presentan una fuerte tendencia hacia la limitación provocada por los accesos a memoria principal. MG, CG, SP y UA encabezan esta tendencia. En el caso de UA es importante saber que la reducción de instrucciones respecto a la versión escalar es pequeña, 17 %. Por tanto, aunque fuera una candidata a mejorar, si la limitación por memoria está presente, mejorar el índice de vectorización no va a erradicar su perfil memory bound. La aplicación IS es interesante porque presenta muchos accesos a memoria por culpa de fallos en ambos niveles de TLB. Estos ciclos, junto a aquellos por fallos en la UL2, hacen un total del 75 % de ciclos de memoria. Por tanto, pese a que esta aplicación se presentaba como una posible candidata, sería necesario aplicar mejoras sobre la jerarquía de memoria en primer lugar. La estadística de EP, con un 35 % de ciclos por dependencias de instrucciones vectoriales, confirma su índice de vectorización del 33 %. Igualmente, no se trata de algo positivo porque no se consigue un buen IPC. Esta aplicación entonces se clasificaría como dependence bound. Finalmente tenemos la aplicación DC, dominada por instrucciones enteras, la cual repite un comportamiento que viene siendo habitual en todos los bechmarks cuando se identifican aplicaciones principalmente enteras, esto es, tener el mejor IPC. Sin embargo, esta vez ni es tan alto ni se refleja en un gran porcentaje de ciclos Issue debido a los fallos de TLB y en la jerarquía de cache. Igual no la podríamos clasificar como memory bound, porque ya se sabía que parte de los ciclos de DL1 que engordan su barra son ocasionados por dependencias con instrucciones de carga de datos. Por ello, se la clasificaría como memory dependence bound. Tras todo este análisis, las aplicaciones seleccionadas son: BT: de Block Tridiagonal, es una aplicación que presenta un solucionador de sistemas no lineales de ecuaciones con derivadas parciales usando matrices de bloques tridiagonales. LU: de Lower-Upper, es una aplicación que tiene el mismo objetivo que BT, es decir, la resolución de un sistema, pero aquí es realizada aplicando el método de factorización LU por Gauss-Seidel SPEC fp Las aplicaciones de más interés mencionadas durante la caracterización fueron namd, povray y milc, porque presentaban un índice de vectorización muy pequeño: 1 %, 2 % y 2,4 %, respectiva-

83 8.1. RESULTADOS 73 mente. Predominaba en todas ellas el porcentaje de instrucciones escalares: 58 %, 32 % y 84 %. Figura 8.9: Versión vectorizada vs no vectorizada de SPEC fp La Figura 8.9 (pág. 73) nos muestra que lbm, teniendo el mejor índice de vectorización, ascendiendo este a un 76 %, no se ve acompañada por una mejora semejante en el número de ciclos. Se clasifica por tanto en el conjunto de aplicaciones memory bound. Independientemente de este hecho, es una ventaja que el único frente que haya que abordar sean los accesos a memoria, ya que la mejora en el número de instrucciones es de un 77 % y el índice de vectorización no deja lugar a dudas de que es una aplicación que hace un uso efectivo de los procesadores vectoriales. Otro caso es el de leslie3d, que muestra una mejora semejante tanto a nivel de instrucciones como de ciclos. En este caso el índice de vectorización ascendía solo al 33 %. Figura 8.10: Ciclos desglosados de las aplicaciones de SPEC fp 2006 Zeusmp y cactusadm son interesantes porque ambas tenían un índice de vectorización semejante: 35 % y 37 %, respectivamente. Sin embargo, zeusmp no mejora tanto respecto al número de ciclos (36 %), categorizándose como memory bound. Por su parte, cactusadm presenta una mejora del 62 %. La aplicación Wrf, comparándola con zeusmp por proximidad, pese a presentar un índice de vectorización del 24 % obtiene mejor resultados respecto a la versión escalar: 73 % de reducción de instrucciones, frente al 72 % de zeusmp, y 40 % de reducción de ciclos, frente al

84 74 CAPÍTULO 8. ESTUDIO EXPERIMENTAL 36 % de zeusmp. Esto significa, que pese a que una aplicación tenga peores resultados respecto al número de instrucciones vectorizadas, su cómputo global respecto a la versión no vectorial puede ser mejor. Gromacs, al igual que cactusadm, tiene incluso una mejor relación entre reducción de ciclos e instrucciones, siendo de un 53 y un 55 % respectivamente. En el extremo del punto (1, 1) se observa la concentración del resto de aplicaciones, algunas más limitadas por memoria y otras sin cambios aparentes, pero en general con mejoras discretas. Fuera de estas, se observan namd y povray sin mejora alguna. Igualmente, este empeoramiento es pequeño de cara al número de instrucciones, siendo de un 0,49 % en namd y un 0,46 % en povray. Lo más significativo es el empeoramiento de povray respecto al número de ciclos, siendo un aumento de un 4,7 %. En la Figura 8.10 (pág. 73), en el extremo de las aplicaciones que no están limitadas por memoria, nos encontramos a calculix. Su elevado IPC era de esperar gracias a las instrucciones enteras que lo componen, suponiendo éstas un 92 % de las instrucciones ejecutadas por la aplicación. De las aplicaciones que le siguen, soplex también es una aplicación mayoritariamente entera, con un 81 % de instrucciones de este tipo. Pese a esto, las instrucciones escalares y vectoriales que aportan un 3 % y un 16 % respectivamente al total, provocan dependencias que impiden obtener un porcentaje de ciclos Issue mayor. Finalmente, comentar que el interés principal de esta gráfica radica en centrarnos en si aquellas que aportan mayor número de ciclos por culpa de dependencias de instrucciones escalares, tienen características destacadas por el resto de gráficas para determinar su candidatura. Es el caso por ejemplo de namd y povray, que se mencionaron en la caracterización. Por tanto, las seleccionadas para su análisis serán: namd: es una aplicación que simula grandes sistemas biomoleculares. povray: realiza un tratamiento sobre una imagen de dimensiones 1280x1024 que contiene un paisaje con objetos abstractos, usando para ello un filtro de ruido denominado Perlin, por su autor Ken Perlin Diagnóstico Software El diagnóstico es un proceso en el que, conociendo a priori el comportamiento de la aplicación, tanto de cara al perfil estático que nos proporciona el compilador como durante su ejecución, nos adentramos en su interior para determinar qué bloques básicos son los que están impidiendo un uso más efectivo de la unidad vectorial. Estos bloques básicos serán los que al final nos den las claves para plantear una posible solución con el interés de incrementar el número de instrucciones vectoriales ejecutadas por la aplicación. La forma de operar en esta sección será la siguiente: se tomarán cada uno de las aplicaciones seleccionadas en la Sección 8.1 (pág. 65) y, para cada una, se consultarán los bloques básicos más ejecutados, se analizarán las porciones de código fuente que se corresponden con ellos en la búsqueda de bucles potencialmente vectorizables, se consultará el resultado del compilador para con los bucles encontrados y se tratará de proponer alguna solución que pudiera ser viable para su mejora. Respecto a las soluciones propuestas, no era objetivo de este trabajo implementarlas, sino dejarlas como una posible guía de cara a trabajos futuros.

85 8.2. DIAGNÓSTICO SOFTWARE Polyhedron Las aplicaciones del benchmark Polyhedron seleccionadas para un análisis en profundidad son: Fatique, Induct, Aermod y Gas dyn. Fatigue Recordando las características que se presentaron de Fatigue durante la caracterización, tenía 91 bucles, de los cuales un 48 % no se había vectorizado. La razón considerada por el compilador era que la vectorización, pese a ser posible, resultaba ineficiente. PC Función Instrucciones % Ciclos % 1 0x407a50 perdida m mp generalized hookes law , ,59 2 0x40238e MAIN , ,1 Tabla 8.1: Bloques 1 y 2 de la lista de bloques básicos más ejecutados en Fatigue, Polyhedron La Tabla 8.1 (pág. 75) muestra los dos bucles más ejecutados de la aplicación. Los campos de esta tabla son: posición del bucle en la lista, PC de la primera instrucción del bloque, nombre de la función, instrucciones ejecutadas, porcentaje respecto al total, ciclos contabilizados y porcentaje respecto al total de la aplicación. El primero de los bloques se corresponde con una función denominada generalized hookes que se encuentra dentro del fichero fatigue.f90 de la aplicación. 1110! FUNCTION GENERALIZED_HOOKES_LAW function generalized_hookes_law ( strain_tensor, lambda, mu) result ( stress_tensor ) Con una primera mirada al código de la función, vemos que está formado por multitud de operaciones realizadas sobre los elementos de un vector de dimensión 6 y de una matriz de dimensiones 6x6. A modo de ejemplo se muestran algunas líneas: 1158 generalized_constitutive_tensor (:,:) = 0.0 _LONGreal 1159 generalized_constitutive_tensor (1,1) = lambda _LONGreal * mu 1160 generalized_constitutive_tensor (1,2) = lambda 1161 generalized_constitutive_tensor (1,3) = lambda 1162 generalized_constitutive_tensor (2,1) = lambda 1163 generalized_constitutive_tensor (2,2) = lambda _LONGreal * mu Además, existe un pequeño bucle dentro que el compilador no ha vectorizado por considerar dicha vectorización como ineficiente do i = 1, generalized_stress_vector ( i) = dot_product ( generalized_constitutive_tensor (i,:), 1185 generalized_strain_vector (:) ) 1186 end do Pese a que 6 son pocas vueltas para un bucle, y que el número de datos en punto flotante es pequeño como para llegar a llenar los 16 que puede albergar un registro vectorial de 512 bits,

86 76 CAPÍTULO 8. ESTUDIO EXPERIMENTAL se podría utilizar el pragma vector always como primera opción de mejora. Al mismo tiempo, se podrían convertir todos las líneas que operan sobre los vectores y las matrices en bucles. Después de revisar el flujo de ejecución del programa, vemos que el bloque correspondiente a esta función solo es accedido desde un único call. La única función que invoca a ésta se denomina perdida. Pese a que no tiene bucles, si se comprueba el bloque desde el que se invoca, descubrimos que en realidad se ha hecho inline en la función main. Este bloque en cuestión se corresponde además con el tercero más ejecutado. Véase la Tabla 8.2 (pág. 76). PC Función Instrucciones % Ciclos % 3 0x401eb4 MAIN , ,1 Tabla 8.2: Bloque 3 de la lista de bloques básicos más ejecutados en Fatigue, Polyhedron A continuación se muestra una pequeña porción de la función main donde se invoca a perdida: 1435 do n = 1, number_of_sample_points 1436 if (. not. failed ( n)) then 1437 call perdida ( dt, lambda, mu, yield_stress, R_infinity, b, 1438 gamma, eta, plastic_strain_threshold, stress_tensor (:,:, n), 1439 strain_tensor (:,:,n), plastic_strain_tensor (:,:,n), 1440 strain_rate_tensor (:,:, n), accumulated_plastic_strain ( n), 1441 back_stress_tensor (:,:, n), isotropic_hardening_stress ( n), 1442 damage ( n), failure_threshold, crack_closure_parameter ) 1443 end if 1444 end do Sin embargo, el compilador indica que el bucle anterior no es un bucle interno: fatigue.f90(1435): (col. 10) remark: loop was not vectorized: not inner loop Revisando de nuevo el código de la función perdida, encontramos operaciones realizadas sobre todos los elementos de una matriz de este modo: 927 stress_tensor (:,:) = (1.0 _LONGreal - damage ) * 928 generalized_hookes_law ( strain_tensor (:,:) plastic_strain_tensor (:,:), lambda, mu) stress_tensor (:,:) = (1.0 _LONGreal - crack_parameter * damage ) / 941 (1.0 _LONGreal - damage ) * stress_tensor (:,:) deviatoric_stress_tensor (:,:) = stress_tensor (:,:) damaged_dev_stress_tensor (:,:) = deviatoric_stress_tensor (:,:) / (1.0 _LONGreal - crack_parameter * damage ) El operador (:, :) del código anterior, usado para operar sobre los elementos de una matriz, es una manera corta de indicar que en realidad la operación tiene que ser realizada sobre todos los elementos. Es por tanto un bucle simplificado. Tiene entonces sentido que el compilador notificara que el bucle que engloba estas líneas no es un bucle interno. En concreto, el segundo bloque con mayor número de instrucciones mostrado en la Tabla 8.1 (pág. 75) se corresponde con las líneas siguientes: 950 deviatoric_stress_tensor (:,:) = stress_tensor (:,:) damaged_dev_stress_tensor (:,:) = deviatoric_stress_tensor (:,:) / (1.0 _LONGreal - crack_parameter * damage )

87 8.2. DIAGNÓSTICO SOFTWARE equivalent_stress = sqrt (1.5 _LONGreal * sum (( damaged_dev_stress_tensor (:,:) back_stress_tensor (:,:) ) **2) ) 962 yield_function = equivalent_stress - isotropic_hardening_stress - yield_stress Para todas ellas el compilador indica lo siguiente: fatigue.f90(950): (col. 7) remark: loop was not vectorized: vectorization possible but seems inefficient fatigue.f90(955): (col. 7) remark: loop was not vectorized: vectorization possible but seems inefficient fatigue.f90(955): (col. 7) remark: loop was not vectorized: vectorization possible but seems inefficient fatigue.f90(960): (col. 46) remark: loop was not vectorized: vectorization possible but seems inefficient fatigue.f90(960): (col. 46) remark: loop was not vectorized: vectorization possible but seems inefficient Hasta ahora hemos encontrado dos puntos de interés en este análisis. El primero fue en la función generalized hookes donde había un pequeño bucle y varios accesos a vectores y matrices completamente desenrollados. Para el bucle, con el pragma vector always podría ser suficiente. Sin embargo, en el caso de los accesos a los vectores y matrices habría que reescribirlos para convertirlos en bucles. El segundo punto de interés estaba en la función perdida y sus accesos a vectores a través del operador (:,:). En este caso, la solución propuesta consistiría en convertir todos accesos en bucles y usar de nuevo el pragma vector always. Induct Induct es una aplicación con reparto bastante homogéneo de las instrucciones en punto flotante. Consta de un 42,3 % de instrucciones vectoriales y un 48,66 % de instrucciones escalares. Estos datos son chocantes si tenemos en cuenta que solo un 16 % de sus 246 bucles había sido vectorizado. Aparte, comparativamente respecto a la versión escalar de la aplicación, seguía a gas dyn en el segmento gracias a la mejora tanto en instrucciones como en ciclos. Sin embargo, el 36 % de ciclos de penalización de que constaba su simulación debido a las dependencias entre instrucciones escalares no podía quedar sin investigar. PC Función Instrucciones % Ciclos % 1 0x mqc m mp mutual ind quad cir coil 2 0x407cc9 mqc m mp mutual ind quad cir coil 4 0x405f18 mqr m mp mutual ind quad rec coil 5 0x406b96 mqr m mp mutual ind quad rec coil , , , , , , , ,8 Tabla 8.3: Bloques 1, 2, 4 y 5 de la lista de bloques básicos más ejecutados en Induct, Polyhedron La Tabla 8.3 (pág. 77) muestra cuatro de los cinco bloques básicos más ejecutados. Las funciones asociadas se encuentran en el único fichero de la aplicación, llamado induct.f90. Vemos que hay dos bloques en cada una de estas funciones con el mismo número de instrucciones ejecutados. La cifra correspondiente a los ciclos no tiene por qué coincidir necesariamente, ya que existe el

88 78 CAPÍTULO 8. ESTUDIO EXPERIMENTAL factor dependencias que ocasiona variaciones en la cuenta. Al comprobar el código de la aplicación correspondiente a las funciones quad cir coil y quad rec coil, vemos que prácticamente eran iguales salvo en alguna línea aislada que no influía en lo que buscábamos. El esquema general de ambas funciones consistía en 2 grupos de 3 bucles encadenados cada una. Hacían un total de 4 grupos, cada uno de ellos asociado a uno de los cuatro bloques de la Tabla 8.3 (pág. 77). A continuación se presenta el contenido de uno de los bucles internos: 1772 do k = 1, q_vector (1) = 0.5 _longreal * a * ( x2gauss ( k) _longreal ) 1774 q_vector (2) = 0.25 _longreal * b2 * ( y2gauss ( k) _longreal ) 1775 q_vector (3) = 0.0 _longreal rot_q_vector (1) = dot_product ( rotate_quad (1,:), q_vector (:) ) 1780 rot_q_vector (2) = dot_product ( rotate_quad (2,:), q_vector (:) ) 1781 rot_q_vector (3) = dot_product ( rotate_quad (3,:), q_vector (:) ) numerator = w1gauss ( j) * w2gauss ( k)* 1786 dot_product ( coil_current_vec, current_vector ) 1787 denominator = sqrt ( dot_product ( rot_c_vector - rot_q_vector, 1788 rot_c_vector - rot_q_vector )) 1789 l12_upper = l12_upper + numerator / denominator end do El informe del compilador indica que se han podido vectorizar los bucles internos presentes en cada uno de los cuatro bloques: induct.f90(1772): (col. 15) remark: LOOP WAS VECTORIZED induct.f90(1772): (col. 15) remark: remainder loop was not vectorized: low trip count induct.f90(1660): (col. 15) remark: LOOP WAS VECTORIZED induct.f90(1660): (col. 15) remark: remainder loop was not vectorized: low trip count induct.f90(2077): (col. 15) remark: LOOP WAS VECTORIZED induct.f90(2077): (col. 15) remark: remainder loop was not vectorized: low trip count induct.f90(2220): (col. 15) remark: LOOP WAS VECTORIZED induct.f90(2220): (col. 15) remark: remainder loop was not vectorized: low trip count Los bloques denominados reminder son aquellos que se generan después del bucle principal en caso de que el compilador, al vectorizar, no haya podido abarcar todos los elementos del vector. En este caso el número de iteraciones que realiza, 9, no es lo suficientemente alto como para generar un bloque reminder adicional. Veamos un pequeño fragmento del primero de los bloques básicos: 1 vaddsd xmm28, k0, xmm26, xmm13 2 vaddsd xmm27, k0, xmm25, xmm14 3 vmulpd zmm14, k0, zmm19, qword ptr [ rax *8+0 x ]{ b} 4 vaddsd xmm30, k0, xmm24, xmm15 5 vbroadcastsd zmm31, k0, xmm28 6 vbroadcastsd zmm1, k0, xmm27 7 vbroadcastsd zmm15, k0, xmm30 8 vsubpd zmm0, k0, zmm31, zmm22 9 vsubpd zmm29, k0, zmm1, zmm21

89 8.2. DIAGNÓSTICO SOFTWARE vmulpd zmm1, k0, zmm14, zmm vmulsd xmm14, k0, xmm29, xmm29 13 vfmadd213sd xmm13, xmm13, xmm14 14 vfmadd213sd xmm15, xmm15, xmm13 15 vsqrtsd xmm15, xmm15, xmm15 16 vdivsd xmm13, xmm3, xmm15 Está bien vectorizado y, como es lógico, necesita intercalar algunas instrucciones escalares a las vectoriales para realizar operaciones intermedias. Esto significa que induct funciona bien y esta haciendo un uso efectivo de la unidad vectorial. Sin embargo, esto no se traduce en un buen IPC debido a las dependencias de tanto las instrucciones escalares como las vectoriales y, dado que hay abundantes escalares en los bloques ya vectorizados, lo que parecía un buen intento de mejorar la aplicación centrándose en estas instrucciones, no ha dado el resultado esperado. Una posible solución que se deja planteada consiste en hacer uso de la directiva unroll para indicar al compilador que desenrolle por 2. Si consigue hacer un reordenamiento adecuado, se reducirían las dependencias y mejoraría el IPC. Aermod Aermod, como vimos en la caracterización, es una aplicación con una gran cantidad de bucles, 2597, y con un índice de vectorización muy pequeño, 5,59 %. Al analizar el listado de bloques básicos con mayor número de instrucciones, vemos que éstos le confieren un perfil plano. Esto significa que el reparto de instrucciones ejecutadas es muy homogéneo no destacando ningún bloque por encima de los demás. Por este motivo, se tomó la decisión de visitar uno a uno todos los bloques que proporcionaban información útil, clasificados por las funciones que los relacionaban entre ellos. Véase a continuación el desglose. PC Función Instrucciones % Ciclos % 1 0x5bd009 powf.l , ,32 2 0x5bd0b0 powf.l , ,18 Tabla 8.4: Bloques 1 y 2 más ejecutados de Aermod, Polyhedron La función powf.l que se muestra en la Tabla 8.4 (pág. 79), es en realidad una función de librería. Para este caso, se buscó la región desde donde se estaban realizando la mayor cantidad de llamadas a la misma. Con una de las herramientas internas que permitía visualizar el control de flujo de cada una de las funciones de la aplicación, era posible hacer una consulta sobre el primero de los bloques de la función para conocer aquellos que contenían llamadas a la misma. El resultado se muestra en la tabla siguiente: PC Función Instrucción Ejecuciones % 0x5ba120 sigz call 0x5ba ,5 Pese a que sea la función sigz la que se indica, después de analizar el código más a fondo se llegó a una función llamada szsfcl, de la que se había hecho inline en sigz. Se muestra a continuación un fragmento de la misma. El operador ** que se visualiza en la función es la potencia: x**n = x n. Las líneas no mostradas son comentarios y declaraciones de variables:

90 80 CAPÍTULO 8. ESTUDIO EXPERIMENTAL SUBROUTINE SZSFCL ( XARG ) IF ( UNSTAB. AND. SURFAC ) THEN SZSURF = BSUBC *( *( CENTER /ZI))** ALPHA1 *( USTAR / UEFFD ) *( USTAR / UEFFD )*( XARG * XARG / ABS ( OBULEN )) ELSEIF ( STABLE ) THEN SZSURF = ( RTOF2 / RTOFPI )* USTAR *( XARG / UEFF ) *( * XARG / OBULEN ) **( ) ELSE SZSURF = ENDIF CONTINUE END Esta función no tiene bucles que tratar, ni siquiera un acceso especial a algún vector o matriz. Asimismo, la función desde donde se invoca, sigz, tampoco tiene. Si continuamos con este mecanismo de comprobar quién invoca a quién, encontramos llamadas a sigz desde funciones que no tienen bucles. Un ejemplo es una función llamada adisz, desde donde se invocaba 26 millones de veces. A ésta también se la llamaba desde una función llamada iblval un total de 18 millones de veces. Ninguna de estas piezas de código contenía bucles, con lo que en principio parecen simples bloques que, por el número de instrucciones que contienen y por las veces que se las invoca desde diferentes funciones, tienen todas las papeletas para aparecer en el top de bloques básicos. Volviendo a los 10 bloques más ejecutados, encontramos tres de ellos pertenecientes a la misma función: anyavg. Véase la Tabla 8.5 (pág. 80). PC Función Instrucciones % Ciclos % 3 0x50db17 anyavg , ,03 5 0x50da34 anyavg , x50d94c anyavg , ,748 Tabla 8.5: Bloques 3, 5 y 10 más ejecutados de Aermod, Polyhedron Estos bloques ni son bucles ni están contenidos dentro de un bucle mayor. Al realizar la búsqueda de los bloques desde donde se invoca a ésta función, encontramos que los más significativos se encuentran contenidos en la función iblval. Dado que iblval se mencionó anteriormente porque aparecía como función raíz desde donde se acababa invocando a powf.l y que, dentro de los 10 bloques más ejecutados aparecen otros que también forman parte de ella, se analizarán para ver si guardan alguna relación con anyavg o powf.l. Véanse en la Tabla 8.6 (pág. 80). PC Función Instrucciones % Ciclos % 7 0x50d260 iblval , ,37 9 0x50d2af iblval , ,37 Tabla 8.6: Bloques 7 y 9 más ejecutados de Aermod, Polyhedron Todos ellos son idénticos en contenido de instrucciones y, al comprobar las líneas de código a las que pertenecen, se descubre que son de una función llamada Locate. Esto significa que, de nuevo, se ha hecho inline de una función desde diferentes puntos. En este caso todos los puntos forman parte de iblval. Véase a continuación un fragmento de la función:

91 8.2. DIAGNÓSTICO SOFTWARE SUBROUTINE LOCATE ( PARRAY, LVLBLW, LVLABV, VALUE, NDXBLW ) IMPLICIT NONE INTEGER LVLABV, LVLBLW, NDXBLW, JL, JM, JU REAL PARRAY ( LVLABV ), VALUE JL = LVLBLW JU = LVLABV DO WHILE ( (JU -JL).GT.1 ) JM = ( JU+JL)/ IF ( VALUE. GE. PARRAY ( JM) ) THEN JL = JM ELSE JU = JM ENDIF ENDDO NDXBLW = JL CONTINUE END Después de encontrar el primer bucle, comprobamos que no se ha vectorizado dando como razón el compilador que no es una estructura soportada: aermod.f90(21743): (col. 7) remark: loop was not vectorized: unsupported loop structure En el bucle se está accediendo al vector parray(jm) dentro de una condición. El índice, jm, se calcula en cada una de las iteraciones, convirtiendo la vectorización en una tarea complicada, ya que habría que calcular previamente todos los índices para poder acceder a varios elementos simultáneamente. Esto se podría traducir en una instrucción de tipo gather que cargara cada uno de los valores del vector según los índices que se hubieran calculado previamente. Sin embargo, el hecho de que dicho cálculo necesite del acceso al vector previamente, impide la vectorización. Relacionando esto con los dos últimos bloques mostrados de la función iblval en la Tabla 8.6 (pág. 80), encontramos que las llamadas a la función Locate están íntimamente relacionadas con las llamadas a la función anyavg. En multitud de ocasiones aparecen bloques en donde, previo a llamar a anyavg, se divisa el bloque correspondiente al inline de Locate. Esta estructura de llamadas se repite 4 veces en todo el código, estando tres de ellas en el interior de la función iblval. Las siguientes líneas muestran la disposición de las llamadas que tienen más peso de entre todo el conjunto de invocaciones del código: CALL LOCATE ( GRIDHT,1, MXGLVL,ZHI, NDXBHI ) CALL LOCATE ( GRIDHT,1, MXGLVL,ZLO, NDXBLO ) NDXALO = NDXBLO CALL ANYAVG ( MXGLVL, GRIDHT, GRIDWS,ZLO, NDXALO,ZHI, NDXBHI, UEFF ) CALL ANYAVG ( MXGLVL, GRIDHT, GRIDSV,ZLO, NDXALO,ZHI, NDXBHI, SVEFF ) CALL ANYAVG ( MXGLVL, GRIDHT, GRIDSW,ZLO, NDXALO,ZHI, NDXBHI, SWEFF ) CALL ANYAVG ( MXGLVL, GRIDHT, GRIDTG,ZLO, NDXALO,ZHI, NDXBHI, TGEFF ) Viendo que se invoca en cuatro ocasiones a anyavg, se propone el siguiente planteamiento: indicar al compilador que haga inline de las llamadas a anyavg con el pragma inline y comprobar su comportamiento. Otra opción seria rescribir el código englobando las llamadas en un bucle. Finalmente, se analizará el bloque mostrado en la Tabla 8.7 (pág. 82).

92 82 CAPÍTULO 8. ESTUDIO EXPERIMENTAL PC Función Instrucciones % Ciclos % 8 0x sigz , ,69 Tabla 8.7: Bloque 8 más ejecutado de Aermod, Polyhedron La función sigz ya se ha mencionado previamente porque se invocaba desde la función iblval. Sin embargo, se descubrió que el bloque mostrado no se corresponde con esta función en concreto, sino con Locate, la cual también se ha mencionado y de la que sabemos que se hace inline en diferentes puntos del código. Dado que en todos los casos iblval resulta ser la raíz común de todo el análisis, podríamos seguir analizando los niveles superiores en la cadena de llamadas, para averiguar qué funciones la invocan y cómo se comportan. Dado que es algo que veníamos haciendo desde el inicio, el panorama se presenta negativo porque de entre las funciones que la invocan ninguna contenía bucles que justifiquen tantas llamadas. Veamos a continuación algunas de las funciones donde se invoca iblval: PC Función Instrucción Ejecuciones % 0x47bf6f pwidth call 0x50b ,1 0x47d009 plumef call 0x50b ,9 0x4a948f aercalc call 0x50b ,04 0x4a0bcf volcalc call 0x50b ,92 Al llegar a este punto se cesó el análisis, ya que se convirtió más en una cruzada de buscar posibles bucles de interés en un código de líneas, que en un estudio de cómo mejorar la aplicación para hacer un uso más efectivo de la unidad vectorial. Igualmente, llama la atención que siendo una aplicación con más de bucles, se den circunstancias como que los bloques de mayor peso en la aplicación no contuvieran más que un bucle o ninguno. Si bien la mayoría de bucles que contenía resultaron ser pequeños y vectorizables, muchos de ellos no se llegaban ni siquiera a ejecutar. Por ende, aermod es una aplicación que pese a haber sido candidata, no se ha obtenido el análisis que se esperaba de ella. Gas dyn Gas dyn es una aplicación que tenía buen índice de vectorización, 39,04 %, y la versión vectorial frente a la escalar daba los mejores resultados de todas las aplicaciones simuladas. Sin embargo el IPC era únicamente 0,19. Por este motivo se tomó la decisión de proceder a su análisis. Instrucciones Versión Escalar % Versión Vectorial % Enteras ,71 Escalares , ,26 Vectoriales , ,04 Total Tabla 8.8: Desglose de instrucciones de las versiones escalar y vectorial de Gas dyn, Polyhedron El desglose de instrucciones de las versiones vectorial y escalar se muestra en la Tabla 8.8 (pág. 82). Nótese que la versión escalar contiene 1 millón de instrucciones vectoriales. Tras realizar comprobaciones varias con el equipo del compilador, no se llego a una conclusión factible del

93 8.2. DIAGNÓSTICO SOFTWARE 83 comportamiento del compilador. Dejándose este detalle a un lado, si dividimos los 24 millones de instrucciones escalares de la versión escalar entre 16, para intentar realizar una aproximación a mano alzada de la reducción, obtenemos 1,5M de instrucciones vectoriales. La versión vectorial contenía 1,3M de instrucciones vectoriales. El objetivo no era realizar ninguna validación, sino ver como, al utilizar registros vectoriales de 512 bits que pueden contener hasta 16 datos en coma flotante de 4 bytes, se ven reducidas las instrucciones escalares y aumentadas las vectoriales. PC Función Instrucciones % Ciclos % 1 0x eos , ,4 3 0x eos , ,9 Tabla 8.9: Bloques 1 y 3 más ejecutados de Gas dyn, Polyhedron En la Tabla 8.9 (pág. 83) se muestran dos de los bloques más ejecutados de la aplicación, formando ambos parte de la función eos. El primer bloque se corresponde con la línea 410 del siguiente código y el tercer bloque con las líneas 407 a SUBROUTINE EOS ( NODES, IENER, DENS, PRES, TEMP, GAMMA, CS, SHEAT, & 361 & CGAMMA, WT) INTEGER NODES, I 391 REAL SHEAT, CGAMMA, WT 392 REAL, DIMENSION ( NODES ) :: IENER, DENS, PRES, TEMP, GAMMA, CS IF ( SHEAT >0.0. AND. CGAMMA >0.0) THEN 407 TEMP (: NODES ) = IENER (: NODES )/ SHEAT 408 PRES (: NODES ) = ( CGAMMA - 1.0) * DENS (: NODES )* IENER (: NODES ) 409 GAMMA (: NODES ) = CGAMMA 410 CS (: NODES ) = SQRT ( CGAMMA * PRES (: NODES )/ DENS (: NODES )) 411 ELSE ENDIF Pese a que no veamos aparentemente un bucle, la utilización de :NODES para acceder a un vector indica implícitamente que se va a realizar la operación a todos los elementos del vector, entendiéndolo el compilador como si fueran varios bucles uno a continuación del otro. Esta característica se denomina Intel R Cilk TM Plus y se utiliza tanto en el compilador ICC como IFORT[inte][intd]. Al comprobar la compilación de esta porción de código, se aprecia que crean bloques prólogo, normal y epílogo para las líneas 407 a 409 por un lado, y para la línea 410 por otro. Para las líneas 407 a 409 creó un fused loop, esto es, fusionó el acceso a los vectores TEMP, PRES y GAMMA en un mismo bloque. Las líneas del informe del compilador son las siguientes: gas dyn.f90(410): (col. 11) remark: unroll factor set to 2 gas dyn.f90(410): (col. 11) remark: LOOP WAS VECTORIZED gas dyn.f90(410): (col. 11) remark: PEEL LOOP WAS VECTORIZED gas dyn.f90(410): (col. 11) remark: REMAINDER LOOP WAS VECTORIZED gas dyn.f90(407): (col. 11) remark: FUSED LOOP WAS VECTORIZED gas dyn.f90(407): (col. 11) remark: PEEL LOOP WAS VECTORIZED gas dyn.f90(407): (col. 11) remark: REMAINDER LOOP WAS VECTORIZED El hecho de que el compilador este generando código separado para los tres primeros vectores por un lado y para el último por otro, es una de las raíces del problema que estábamos viendo en la Sección (pág. 65), sobre la gran cantidad de ciclos ocasionados por los accesos a la UL2. En la línea 410 de la función se está accediendo a los vectores PRES y DENS después de haber sido accedidos en la línea 408. Como la 410 tiene que trabajar con ellos, se traduce en que la vectorización no hace un buen aprovechamiento de la localidad temporal de la cache, en

94 84 CAPÍTULO 8. ESTUDIO EXPERIMENTAL detrimento del rendimiento global de la aplicación. La solución, que en este caso sí se implemento debido a su sencillez, consistió en rescribir el código transformando los accesos a los bucles usando :nodes por un bucle simple 1..n, manteniendo el desenrollamiento en factor 2 que generaba el compilador. Véase a continuación el código resultante y consúltense la directivas de Fortran en la Sección 4.5 (pág. 29). 411! DIR$ UNROLL (2) 412 DO 10 I = 1, NODES 413 TEMP (I) = IENER (I)/ SHEAT 414 PRES (I) = ( CGAMMA - 1.0) * DENS (I)* IENER (I) 415 GAMMA ( I) = CGAMMA 416 CS(I) = SQRT ( CGAMMA * PRES (I)/ DENS (I)) CONTINUE Al convertir el acceso a los vectores en un bucle explícito que itera uno a uno sobre todos los elementos, el compilador no generará dos secciones separadas para el bucle. Se llevó a la práctica y se obtuvieron los siguientes resultados de la Figura 8.11 (pág. 84) y el mensaje del compilador siguiente: gas dyn.f90(412): (col. 14) remark: unroll factor set to 2 gas dyn.f90(412): (col. 14) remark: PARTIAL LOOP WAS VECTORIZED gas dyn.f90(412): (col. 14) remark: PEEL LOOP WAS VECTORIZED gas dyn.f90(412): (col. 14) remark: REMAINDER LOOP WAS VECTORIZED Figura 8.11: Comparación entre las versiones :nodes y do de gas dyn En primer lugar se aprecia que la línea del código a la que hace referencia es únicamente la 412 de inicio del bucle, aparte de que genera solamente tres bloques. En la Figura 8.11 (pág. 84) se presenta una reducción del 6,51 % en el total de ciclos. Esta reducción es debido a la disminución de accesos en la UL2 en un 16,09 %. Ahora sí se está realizando un aprovechamiento de la localidad temporal, mejorando por tanto el rendimiento de la aplicación Mantevo La aplicación del benchmark Mantevo seleccionada para un análisis en profundidad es CoMD.

95 8.2. DIAGNÓSTICO SOFTWARE 85 CoMD La caracterización y los resultados obtenidos por el simulador CMP$im modificado, muestran a CoMD como una aplicación complicada de abordar. Su 0,22 % de instrucciones vectoriales y su 30,43 % de instrucciones escalares da poco margen para realizar cambios. Aparte, el resultado de la comparación entre las versiones escalares y vectoriales mostraban que vectorizar la aplicación no servía sino para empeorar el rendimiento de la misma tanto en número de instrucciones ejecutadas como en ciclos consumidos. Estudiando los bloques con más peso en el programa, encontramos que la sección de código fundamental en donde se está realizando la mayor parte del cómputo son los siguientes bucles encadenados: 188 for ( ioff = ibox * MAXATOMS,ii =0; ii < nibox ; ii ++, ioff ++) { /* loop over atoms in ibox */ 189 int joff ; 191 s-> stress -= s->p[ ioff ][0]*s->p[ ioff ][0]/s-> mass [ ioff ]; 192 int i = s- >id[ ioff ]; /* the ij - th atom in ibox */ 193 for ( joff = MAXATOMS *jbox,ij =0; ij < njbox ; ij ++, joff ++) { /* loop over atoms in ibox */ 194 int m; 195 real_t dr [3]; 196 int j = s- >id[ joff ]; /* the ij - th atom in ibox */ 197 if ( j <= i ) continue ; 198 r2 = 0.0; 199 for (m =0; m <3; m ++) { 200 dr[m] = drbox [m]+s->r[ ioff ][m]-s->r[ joff ][m]; 201 r2 += dr[m]* dr[m]; 202 } if ( r2 > r2cut ) continue ; r2 =( real_t ) 1.0/ r2; 214 r6 = (r2*r2*r2); 216 s->f[ ioff ][3] += 0.5* r6 *( s6*r6-1.0) ; 217 s->f[ joff ][3] += 0.5* r6 *( s6*r6-1.0) ; 218 etot += r6 *( s6* r6-1.0) - eshift ; fr = - 4.0* epsilon *s6*r6*r2 *(12.0* s6*r6-6.0) ; 222 for (m =0; m <3; m ++) { 223 s->f[ ioff ][m] += dr[m]* fr; 224 s->f[ joff ][m] -= dr[m]* fr; 225 } 226 s-> stress += 1.0* fr*dr [0]* dr [0]; } /* loop over atoms in jbox */ 233 } /* loop over atoms in ibox */ Estos bucles se encargan de computar la interacción entre los átomos dentro de contenedores denominados boxes. El recorrido sobre todos los boxes se realiza con otros dos bucles encadenados que no se visualizan aquí pero que engloban a estos, haciendo un total de 4 bucles. El contenido completo de esta sección de código se puede consultar en el fichero ljforce.c de la aplicación. Los diversos bloques generados por el compilador presentan en su mayoría instrucciones enteras intercaladas con escalares, como por ejemplo el siguiente: 1 mov rbx, qword ptr [ rbp +0 x58 ] 2 vaddss xmm8, xmm3, dword ptr [ rbx +r8 *1+0 x4] 3 vaddss xmm1, xmm4, dword ptr [ rbx +r8 *1] 4 vaddss xmm9, xmm2, dword ptr [ rbx +r8 *1+0 x8] 5 lea r10, ptr [ rdx + rbx *1] 6 vsubss xmm8, xmm8, dword ptr [ r10 + r14 *1+0 x4] 7 vsubss xmm1, xmm1, dword ptr [ r10 + r14 *1] 8 vsubss xmm9, xmm9, dword ptr [ r10 + r14 *1+0 x8] 9 vmulss xmm11, xmm8, xmm8 10 vfmadd231ss xmm11, xmm1, xmm1

96 86 CAPÍTULO 8. ESTUDIO EXPERIMENTAL 11 vfmadd231ss xmm11, xmm9, xmm9 12 vcmpss k0, k0, xmm11, xmm17, 0xe 13 kortestw k0, k0 14 jnz 0 x4049ab El problema que encuentra el compilador para vectorizar el código, son las dependencias entre iteraciones: ljforce.c(193): (col. 3) remark: loop was not vectorized: existence of vector dependence Estas dependencias se producen porque los boxes que se tratan podrían coincidir, es decir, cuando ibox == jbox. En este caso las variables ioff y joff se iniciarían con el mismo valor y el compilador lo está detectando como una dependencia. Una idea para solucionarlo radicaría en rescribir el código generando dos situaciones separadas: una en la que ibox fuera igual a jbox, en cuyo caso seguiría sin vectorizar porque se seguirían produciendo las mismas dependencias, y un else donde incluyéramos los pragmas ivdep y vector always, ya que sabríamos que no se producirían dependencias y es seguro usarlas. El esquema general sería el siguiente: if ( ibox == jbox ) for ( ioff = ibox * MAXATOMS,ii =0; ii < nibox ; ii ++, ioff ++) { /* loop over atoms in ibox */... for ( joff = MAXATOMS *ibox,ij =0; ij < nibox ; ij ++, joff ++) { /* loop over atoms in ibox */... else # pragma ivdep # pragma vector always for ( ioff = ibox * MAXATOMS,ii =0; ii < nibox ; ii ++, ioff ++) { /* loop over atoms in ibox */... for ( joff = MAXATOMS *jbox,ij =0; ij < njbox ; ij ++, joff ++) { /* loop over atoms in ibox */... A la hora de aplicar la solución, es necesario tener en cuenta que la aplicación no ofrece muchas más oportunidades para vectorizar. Cuando tratamos con aplicaciones como esta en la que más de la mitad del código está formado por instrucciones enteras, 69,35 %, las versiones escalares y vectoriales serán muy semejantes y el uso de la unidad vectorial, pese a que puede mejorar, no lo hará mucho Sequoia Las aplicaciones del benchmark Sequoia seleccionadas para un análisis en profundidad son: Crystalmk y SPhotmk. Crystalmk La caracterización mostraba que el 46,7 % de las escalares, frente al 5,8 % de las vectoriales, era un porcentaje interesante sobre el que centrarse de cara a su reducción. Además, en la Sección (pág. 70) se vio que la versión vectorial respecto a la escalar no había obtenido buenos resultados, ya que la relación de instrucciones y ciclos era de 1,13 y 1,15 % respectivamente. Por tanto, después de analizar el conjunto de bloques básicos más ejecutados, la principal diferencia entre ambas

97 8.2. DIAGNÓSTICO SOFTWARE 87 versiones radica en el procedimiento Crystal div del fichero Crystal div.c. Véase a continuación los bucles de las líneas 43, 49, 53, 61 y 65 del código de la función: 16 void Crystal_div ( int nslip, 17 double deltatime, 18 double sliprate [ MS_XTAL_NSLIP_MAX ], 19 double dsliprate [ MS_XTAL_NSLIP_MAX ], 20 double tau [ MS_XTAL_NSLIP_MAX ], 21 double tauc [ MS_XTAL_NSLIP_MAX ], 22 double rhs [ MS_XTAL_NSLIP_MAX ], 23 double dtcdgd [ MS_XTAL_NSLIP_MAX ][ MS_XTAL_NSLIP_MAX ], 24 double dtdg [ MS_XTAL_NSLIP_MAX ][ MS_XTAL_NSLIP_MAX ], 25 double matrix [ MS_XTAL_NSLIP_MAX ][ MS_XTAL_NSLIP_MAX ]) { 28 double bor_array [ MS_XTAL_NSLIP_MAX ]; 29 double sgn [ MS_XTAL_NSLIP_MAX ]; 30 double ratefact [ MS_XTAL_NSLIP_MAX ]; 31 double taun [ MS_XTAL_NSLIP_MAX ]; 32 double err [ MS_XTAL_NSLIP_MAX ]; double rate_offset = 1. e -6; 35 double taua = 30.; 36 double tauh = 1.2; 37 double rate_exp = 0.01; 38 double bor_s_tmp = 0.0; int n = 0; 41 int m = 0; for ( n = 0; n < nslip ; n ++) { 44 sgn [ n] = 1.0; 45 ratefact [ n] = (0.2 * n) / MS_XTAL_NSLIP_MAX ; 46 } // ---- MS_Xtal_PowerTay 49 for ( n = 0; n < nslip ; n ++) { 50 bor_array [ n] = 1 / ( sliprate [ n]* sgn [ n] + rate_offset ); 51 } for ( n = 0; n < nslip ; n ++) { 54 tau [ n] = taua * ratefact [ n] * sgn [ n]; 55 for ( m = 0; m < nslip ; m ++) 56 dtcdgd [ n][ m] = tauh * deltatime * ratefact [ n]; 57 dtcdgd [ n][ n] += tau [ n] * rate_exp * sgn [ n] * bor_array [ n]; 58 } // MS_Xtal_SlipRateCalc 61 for ( n = 0; n < nslip ; n ++) { 62 bor_array [n] = 1/ dtcdgd [n][n]; 63 } for ( n = 0; n < nslip ; n ++) { 66 taun [n] = tau [n]; 67 for ( m = 0; m < nslip ; m ++) { 68 bor_s_tmp = dtdg [ n][ m]* deltatime ; 69 taun [ n] += bor_s_tmp * dsliprate [ m] ; 70 matrix [n][m] = (- bor_s_tmp + dtcdgd [n][m])* bor_array [n]; 71 } 72 err [n] = taun [n] - tauc [n]; 73 rhs [n] = err [n] * bor_array [n]; } } En la versión escalar, cuando el compilador genera el código para este procedimiento, identifica que todos los bucles iteran con la misma variable y por tanto condensa todas las operaciones en

98 88 CAPÍTULO 8. ESTUDIO EXPERIMENTAL el mismo bucle. Así se consigue eliminar código de control que hubiese estado repetido en cada uno de los bucles. El bloque generado lo denomina fused loop y consta de instrucciones. En la versión vectorial una parte de este bucle es vectorizada mientras que otra no, ocasionando que las instrucciones del bloque escalar ahora se repartan en tres bloques diferentes. El bloque más grande de los tres se corresponde con los bucles de las líneas 61 y 65. En el Capítulo 6 (pág. 39) comprobamos lo habitual de la situación en la que el compilador intenta vectorizar el bucle interno. Sabemos que si lo consigue indicará que el bucle externo no se ha vectorizado por no ser un bucle interno, not inner loop. En el caso del bucle de la línea 65, que contiene un bucle interno que empieza en la línea 67, ocurría algo distinto. El compilador estaba intentando vectorizar desde el bucle externo: Crystal div.c(65): (col. 4) remark: loop was not vectorized: existence of vector dependence Esta dependencia era de esperar porque en el bucle interno se realizan tantos cálculos sobre el elemento n del vector taun como indica la variable nslip, antes de volver a ser utilizado en el bucle externo. La solución consistiría en indicar al compilador que vectorice el bucle interno con el pragma vector always, puesto que dentro del bucle interno no hay dependencias. SPhotmk El índice de vectorización de SPhotmk era bajo, 5,56 %, frente a un 50,5 % de instrucciones escalares. La comparativa entre las versiones escalar y vectorial mostraba que no se obtenía ni reducción de instrucciones ni de ciclos. En la Tabla 8.10 (pág. 88) figuran los bloques con mayor peso de cómputo. Entre ellos encontramos los de la librería logaritmo pertenecientes a la función log.l. Cuando parte de los bloques más ejecutados provienen de librerías como ésta, un modo de intentar buscar una solución para vectorizar consistiría en conseguir que el compilador invoque la función de la librería adaptada al cálculo sobre vectores, siempre y cuando estuviera disponible. PC Función Instrucciones % Ciclos % 1 0x46dda3 log.l , ,39 2 0x execute , ,99 3 0x40581b execute , ,19 4 0x46dd20 log.l , ,02 Tabla 8.10: Bloques 1, 2, 3 y 4 más ejecutados de SPhotmk, Sequoia Los bloques básicos situados en las posiciones 2 y 3 se corresponden con las funciones ranfmodmult del fichero random.f y execute del fichero execute.f respectivamente. Para ambos ficheros se presentan dos pequeños extracto a continuación: random.f 338 subroutine ranfmodmult ( A, B, C ) dimension A( IRandNumSize ), B( IRandNumSize ), C( IRandNumSize ) a1 = A (1) 362 a2 = A (2) 363 a3 = A (3) 364 b1 = B (1) 365 b2 = B (2) 366 b3 = B (3) 367 j1 = a1 * b1

99 8.2. DIAGNÓSTICO SOFTWARE j2 = a1 * b2 + a2 * b1 369 j3 = a1 * b3 + a2 * b2 + a3 * b1 370 j4 = a1 * B( 4 ) + a2 * b3 + a3 * b2 + A( 4 ) * b k1 = j1 373 k2 = j2 + k1 / k3 = j3 + k2 / k4 = j4 + k3 / C( 1 ) = mod ( k1, 4096 ) 378 C( 2 ) = mod ( k2, 4096 ) 379 C( 3 ) = mod ( k3, 4096 ) 380 C( 4 ) = mod ( k4, 4096 ) return 383 end execute.f 372 dist = - log ( ranf ( myseed )) / sig ( ir, ig)! distance to collision 373 dcen = ( tcen - age ) * d10! distance to census El fichero principal es excecute.f. Este contiene una serie de bucles encadenados que abarcan la mayor parte de la aplicación. Un código de estas características dificulta la vectorización porque para todos los bucles el compilador indica que no se ha vectorizado ninguno por no ser bucles internos. Cuando al final llega al bucle más interno, resulta que no lo vectoriza debido a la existencia de dependencias. Éstas fueron muy difíciles de identificar: execute.f(271): (col. 19) remark: loop was not vectorized: existence of vector dependence execute.f(268): (col. 16) remark: loop was not vectorized: not inner loop execute.f(193): (col. 16) remark: loop was not vectorized: not inner loop execute.f(154): (col. 13) remark: loop was not vectorized: not inner loop execute.f(103): (col. 10) remark: loop was not vectorized: not inner loop Las dos líneas de código presentadas de excecute.f se encuentran en el interior del bucle más externo de dos bucles anidados. Contienen las funciones log y ranf. La función ranf es la encargada de invocar a la función ranfmodmult que mencionamos previamente. Por tanto, estas dos líneas son responsables de que los bloques más ejecutados sean los mostrados en el top de bloques básicos. La búsqueda de soluciones se dificulta cuando todos los datos tratados en la función ranfmodmult resultan ser enteros. Esto viene a decir que uno de los bloques con mayor número de instrucciones ejecutadas en el programa, participa dentro del 44 % de instrucciones enteras presentes en la aplicación. Por estos motivos, para mejorarla no basta simplemente con modificar ligeramente el código o incluir pragmas en los bucles más externos. En este caso se propone hacer un estudio más exhaustivo del código y proceder a reescribirlo en la mayor medida posible NPB Las aplicaciones del benchmark NPB seleccionadas para un análisis en profundidad son: BT y UT. BT La aplicación BT fue seleccionada por su elevado porcentaje de instrucciones escalares, 71,85 %. En la Tabla 8.11 (pág. 90) se encuentra el primer y único bloque básico de los bloques más

100 90 CAPÍTULO 8. ESTUDIO EXPERIMENTAL ejecutados que se va a analizar. Es un bloque importante porque supone un 56.2 % del montante total de instrucciones de la aplicación. PC Función Instrucciones % Ciclos % 1 0x4060c0 binvcrhs , ,5 Tabla 8.11: Bloque 1 de los más ejecutados de BT, NPB El código generado por el compilador está formado por 6 instrucciones enteras, 596 escalares y 14 vectoriales, de las cuales solo 3 utilizan vectores de 512 bits. Su código pertenece enteramente a una función denominada binvcrhs que se encuentra en el fichero solve subs.f. Este fichero recoge todas las funciones de interés que se invocan desde el código contenido en los ficheros x solve vec.f, y solve vec.f y z solve vec.f para la versión vectorizada, y desde x solve.f, y solve.f y z solve.f para la no vectorizada. La peculiaridad de estos ficheros es que en las versiones vectorizadas los bucles que contienen las llamadas a binvcrhs traen ya el pragma ivdep incorporado. Véase en el fragmento de código siguiente: 344! dir$ ivdep 345 do i = 1, grid_points (1) call binvcrhs ( lhs (1,1,bb,i,0), 347 > lhs (1,1,cc,i,0), 348 > rhs (1,i,j,0) ) 349 enddo Sin embargo, no se obtiene el resultado esperado por parte del compilador: x solve vec.f(347): (col. 18) remark: vectorization support: call to function binvcrhs cannot be vectorized x solve vec.f(346): (col. 10) remark: loop was not vectorized: existence of vector dependence Analizando el código de la función vemos que no tiene ningún bucle sobre el que trabajar. El único bucle es el que aparece en el fragmento de código anterior desde el que se realiza la llamada. Véase la siguiente porción de la función binvcrhs: 206 subroutine binvcrhs ( lhs,c, r ) dimension lhs (5,5) 219 double precision c (5,5), r (5) pivot = 1.00 d0/ lhs (1,1) 226 lhs (1,2) = lhs (1,2) * pivot 227 lhs (1,3) = lhs (1,3) * pivot 228 lhs (1,4) = lhs (1,4) * pivot 229 lhs (1,5) = lhs (1,5) * pivot 230 c (1,1) = c (1,1) * pivot 231 c (1,2) = c (1,2) * pivot 232 c (1,3) = c (1,3) * pivot 233 c (1,4) = c (1,4) * pivot 234 c (1,5) = c (1,5) * pivot 235 r (1) = r (1) * pivot coeff = lhs (2,1) 238 lhs (2,2) = lhs (2,2) - coeff * lhs (1,2) 239 lhs (2,3) = lhs (2,3) - coeff * lhs (1,3) 240 lhs (2,4) = lhs (2,4) - coeff * lhs (1,4) 241 lhs (2,5) = lhs (2,5) - coeff * lhs (1,5) 242 c (2,1) = c (2,1) - coeff *c (1,1) 243 c (2,2) = c (2,2) - coeff *c (1,2)

101 8.2. DIAGNÓSTICO SOFTWARE c (2,3) = c (2,3) - coeff *c (1,3) 245 c (2,4) = c (2,4) - coeff *c (1,4) 246 c (2,5) = c (2,5) - coeff *c (1,5) 247 r (2) = r (2) - coeff *r (1) Los diversos accesos a los vectores que se muestran representan bucles que se han desenrollado manualmente y de forma completa. La idea que se propone es generar bucles en todas aquellas secciones de código de la función binvcrhs donde sea posible. De este modo el compilador puede intentar vectorizar dichos bucles y, a partir de ese punto, se puede afrontar el análisis de acuerdo a las dificultades que haya encontrado el compilador para no vectorizar. LU La aplicación LU, al igual que BT, fue seleccionada por su porcentaje de instrucciones escalares, 50,4 %. En la Tabla 8.12 (pág. 91) se muestran únicamente los dos primeros bloques del conjunto de bloques básicos más ejecutados, porque destacan por encima de los demás al contener un 23 % y 21 % del total de instrucciones ejecutadas en el programa, respectivamente. PC Función Instrucciones % Ciclos % 1 0x41751b buts , ,0 2 0x41c8c8 blts , ,30 Tabla 8.12: Bloques 1 y 2 de los más ejecutados de LU, NPB El primero contiene parte del código de la función buts del fichero buts vec.f y el segundo bloque parte de blts del fichero blts vec.f. Ambas funciones tienen la misma estructura, pero cada una se encarga de trabajar con una de las dos matrices U y L, respectivamente. El código se compone de dos secciones diferenciadas: una primera con varios bucles encadenados que el compilador consigue vectorizar, y una segunda que está desenrollada manualmente en un factor de 5. Esta segunda situación es semejante a la que vimos en BT. A continuación se muestra un pequeño fragmento esto último: 77!! dir$ unroll 5 78! manually unroll the loop 79! do m = 1, 5 80 tv( 1, i, j ) = tv( 1, i, j ) 81 > + omega * ( udy ( 1, 1, i, j ) * v( 1, i, j+1, k ) 82 > + udx ( 1, 1, i, j ) * v( 1, i+1, j, k ) 83 > + udy ( 1, 2, i, j ) * v( 2, i, j+1, k ) 84 > + udx ( 1, 2, i, j ) * v( 2, i+1, j, k ) 85 > + udy ( 1, 3, i, j ) * v( 3, i, j+1, k ) 86 > + udx ( 1, 3, i, j ) * v( 3, i+1, j, k ) 87 > + udy ( 1, 4, i, j ) * v( 4, i, j+1, k ) 88 > + udx ( 1, 4, i, j ) * v( 4, i+1, j, k ) 89 > + udy ( 1, 5, i, j ) * v( 5, i, j+1, k ) 90 > + udx ( 1, 5, i, j ) * v( 5, i+1, j, k ) ) 91 tv( 2, i, j ) = tv( 2, i, j ) 92 > + omega * ( udy ( 2, 1, i, j ) * v( 1, i, j+1, k ) 93 > + udx ( 2, 1, i, j ) * v( 1, i+1, j, k ) 94 > + udy ( 2, 2, i, j ) * v( 2, i, j+1, k ) 95 > + udx ( 2, 2, i, j ) * v( 2, i+1, j, k ) 96 > + udy ( 2, 3, i, j ) * v( 3, i, j+1, k ) 97 > + udx ( 2, 3, i, j ) * v( 3, i+1, j, k ) 98 > + udy ( 2, 4, i, j ) * v( 4, i, j+1, k ) 99 > + udx ( 2, 4, i, j ) * v( 4, i+1, j, k ) ! end do

102 92 CAPÍTULO 8. ESTUDIO EXPERIMENTAL El compilador indica que parte de este código es vectorizado como bloques en vez de como bucle. buts vec.f(80): (col. 19) remark: BLOCK WAS VECTORIZED buts vec.f(312): (col. 13) remark: BLOCK WAS VECTORIZED blts vec.f(83): (col. 19) remark: BLOCK WAS VECTORIZED Del resto de partes del código que también se encuentran desenrolladas no muestra ningún mensaje de por qué no ha vectorizado. Por tanto, la idea que se plantea consiste en aplicar la misma solución que para BT, para comprobar si transformando el código desenrollado en un bucle y utilizando el pragma vector always, o el pragma ivdep, se consigue vectorizar SPEC fp Las aplicaciones del benchmark SPEC fp seleccionadas para un análisis en profundidad son: Namd y Povray. Namd La aplicación Namd se caracterizaba por tener unicamente un 1,02 % de instrucciones vectoriales. Además, su 58 % de instrucciones escalares generaba una gran cantidad de dependencias que reducían el IPC de la aplicación. En concreto, estas dependencias ocasionaban que un 48,33 % de los ciclos de la aplicación fueran por este motivo. En el código fuente de Namd existe un fichero denominado ComputeNonbondedBase2.h, sobre el que bascula gran parte del código generado. A continuación se muestra un pequeño fragmento inicial de dicho código: 7 EXCLUDED ( FAST ( foo bar ) ) 8 EXCLUDED ( MODIFIED ( foo bar ) ) 9 EXCLUDED ( NORMAL ( foo bar ) ) 10 NORMAL ( MODIFIED ( foo bar ) ) for ( k =0; k< npairi ; ++k) { const int j = pairlisti [ k]; 15 register const CompAtom * p_j = p_1 + j; register const BigReal p_ij_x = p_i_x - p_j - > position. x; 18 register BigReal r2 = p_ij_x * p_ij_x ; 19 register const BigReal p_ij_y = p_i_y - p_j - > position. y; 20 r2 += p_ij_y * p_ij_y ; 21 register const BigReal p_ij_z = p_i_z - p_j - > position. z; 22 r2 += p_ij_z * p_ij_z ; FAST ( 29 const LJTable :: TableEntry * lj_pars = 30 lj_row + 2 * mol - > atomvdwtype ( p_j - >id) MODIFIED (+ 1); SHORT ( 36 const BigReal * const fast_i = table_four + 16* table_i + 8; 37 BigReal fast_a = fast_i [0]; 38 ) 39 ) 40 FULL ( 41 const BigReal * const scor_i = table_four + 16* table_i + 8 SHORT (+ 4); 42 BigReal slow_a = scor_i [0]; 43 ) r2f.i &= 0 xfffe0000 ;

103 8.2. DIAGNÓSTICO SOFTWARE 93 Véanse las macros FAST y FULL en este fragmento. Estas, además de otras macros que aparecen en el mismo bucle, determinan qué secciones de la función son compiladas dependiendo de donde se usen. En todas las regiones donde se hace uso de alguna de ellas se hace inline del bucle con el código correspondiente a la macro. Esto da lugar a que en el informe del compilador haya múltiples resultados registrados para este bucle en concreto. Cuando consultamos los bloques básicos con mayor número de instrucciones y ciclos, nos encontramos que muchos coincidían debido a lo mencionado sobre el inline de las macros en el bucle. Además, todos tenían instrucciones escalares. Consultando las razones del compilador para no vectorizar este bucle en ninguna de las ocasiones, encontramos una única razón: las dependencias. ComputeNonbondedBase2.h(12): (col. 5) remark: loop was not vectorized: existence of vector dependence ComputeNonbondedBase2.h(76): (col. 7) remark: vector dependence: assumed FLOW dependence between f j line 76 and p j line 21 ComputeNonbondedBase2.h(21): (col. 47) remark: vector dependence: assumed ANTI dependence between p j line 21 and f j line 76 ComputeNonbondedBase2.h(76): (col. 7) remark: vector dependence: assumed FLOW dependence between f j line 76 and f i line 76 ComputeNonbondedBase2.h(76): (col. 7) remark: vector dependence: assumed ANTI dependence between f i line 76 and f j line 76 Según el compilador, las dependencias se encuentran entre las instrucciones p j y f j, así como f j y f i. En el caso de p j y f j, encontramos que p j era un puntero a un objeto de una clase llamada CompAtom, que tomaba su valor de la variable p 1. La variable f j era una referencia a un objeto de una clase llamada Force que tomaba su valor de f 1[j]. El problema radicaba en que se estaba leyendo la posición x de p j y también se estaba escribiendo la posición x de f j. Para el compilador existía una ambigüedad respecto a si las direcciones donde estaban apuntando ambas variables era la misma. register const CompAtom * p_j = p_1 + j; register const BigReal p_ij_x = p_i_x - p_j - > position. x; Force & f_j = f_1 [ j]; register BigReal tmp_x = force_r * p_ij_x ; f_j.x -= tmp_x ; const CompAtom * p_1 = params - >p [1]; Force * f_1 = params - >ff [1]; La variable params es un puntero a la estructura de tipo nonbonded, la cual efectivamente tiene dos campos p y ff de tipos CompAtom y Force respectivamente. 21 struct nonbonded { 22 CompAtom * p [2]; 23 Force * ff [2]; 25 Force * fullf [2]; }; Podemos pues asegurar que tanto p j como f j son dos variables que apuntan a zonas de memoria diferentes y no hay dependencia alguna. Entonces, para conseguir vectorizar se podría hacer uso del pragma ivdep que indica que se ignoren las dependencias. También se podría modificar el código de modo que el procesador entienda que tanto p j como f j son diferentes. Por ejemplo, se podría encerrar el código bajo la condición de que ambas variables fueran diferentes, lo cual sería cierto siempre.

104 94 CAPÍTULO 8. ESTUDIO EXPERIMENTAL Povray Las instrucciones y ciclos de esta aplicación están muy repartidas entre todos los bloques que la componen. Pese a esto, el primer bloque del conjunto de bloques más ejecutados que se muestra en la Tabla 8.13 (pág. 94) presentaba un porcentaje mayor de instrucciones y ciclos que los siguientes en la lista. Por este motivo se procede a su análisis. PC Función Instrucciones % Ciclos % 1 0x5fad40 pov::all Sphere Intersections (pov::object Struct*, pov::ray Struct*, pov::istack struct*) , ,22 Tabla 8.13: Bloques 1 y 2 de los más ejecutados de Povray, SPEC FP De este bloque fue muy útil consultar la información generada por otra de las herramientas internas disponibles. A continuación se presentan dichas instrucciones junto con el detalle de las líneas del código a las que pertenecen: 1 sub rsp, 0 x58 # spheres. cpp :123 2 add qword ptr [ rip +0 x1e8d24 ], 0x1 # frame. h :980 3 mov r8, rsi # spheres. cpp :123 4 vmovsd xmm2, qword ptr [ rdi +0 x80 ] # vector. h :90 5 xor eax, eax # spheres. cpp :129 6 vmovsd xmm3, qword ptr [ rdi +0 x78 ] # vector. h :89 7 vmovsd xmm0, qword ptr [ rdi +0 x88 ] # vector. h :91 8 vmovsd xmm4, qword ptr [ rdi +0 x90 ] # spheres. cpp :131 9 vmulsd xmm7, xmm4, xmm4 # vector. h : vsubsd xmm16, k0, xmm2, qword ptr [ r8 +0 x8] # vector. h :90 11 vsubsd xmm5, xmm3, qword ptr [ r8] # vector. h :89 12 vsubsd xmm9, xmm0, qword ptr [ r8 +0 x10 ] # vector. h :91 13 vmulsd xmm8, k0, xmm16, xmm16 # vector. h : vmulsd xmm6, k0, xmm16, qword ptr [ r8 +0 x20 ] 15 vfmadd231sd xmm8, xmm5, xmm5 16 vmovsd xmm4, qword ptr [ r8] # vector. h :89 17 vmovsd xmm3, qword ptr [ r8 +0 x8] # vector. h :90 18 vmovsd xmm2, qword ptr [ r8 +0 x10 ] # vector. h :91 19 vfmadd231sd xmm8, xmm9, xmm9 # vector. h : vfmadd231sd xmm6, xmm5, qword ptr [ r8 +0 x18 ] 21 vmovsd xmm0, qword ptr [ r8 +0 x20 ] 22 vmovsd xmm1, qword ptr [ r8 +0 x28 ] 23 vcmpsd k0, k0, xmm8, xmm7, 0xd # spheres. cpp : vfmadd132sd xmm9, xmm6, qword ptr [ r8 +0 x28 ] # vector. h : kortestw k0, k0 # spheres. cpp : jz 0 x5fadf4 Las instrucciones terminadas en sd son escalares. Los ficheros que participan en el bloque son spheres.cpp, vector.h y frame.h. Véase primero un fragmento de la función All Sphere Intersections del fichero spheres.h: spheres. cpp 122 static int All_Sphere_Intersections ( OBJECT * Object, RAY * Ray, ISTACK * Depth_Stack ) 123 { 124 register int Intersection_Found ; 125 DBL Depth1, Depth2 ; 126 VECTOR IPoint ; 127 SPHERE * Sphere = ( SPHERE *) Object ; 128

105 8.2. DIAGNÓSTICO SOFTWARE Intersection_Found = false ; if ( Intersect_Sphere (Ray, Sphere -> Center, Sqr ( Sphere -> Radius ), & Depth1, & Depth2 )) 132 { 133 if (( Depth1 > DEPTH_TOLERANCE ) && ( Depth1 < Max_Distance )) 134 { 135 VEvaluateRay ( IPoint, Ray -> Initial, Depth1, Ray -> Direction ); if ( Point_In_Clip ( IPoint, Object -> Clip )) 138 { 139 push_entry ( Depth1, IPoint, Object, Depth_Stack ); Intersection_Found = true ; 142 } 143 } if (( Depth2 > DEPTH_TOLERANCE ) && ( Depth2 < Max_Distance )) 146 { 147 VEvaluateRay ( IPoint, Ray -> Initial, Depth2, Ray -> Direction ); if ( Point_In_Clip ( IPoint, Object -> Clip )) 150 { 151 push_entry ( Depth2, IPoint, Object, Depth_Stack ); Intersection_Found = true ; 154 } 155 } 156 } return ( Intersection_Found ); 159 } La línea principal es la 131 porque contiene la llamada a la función Intersect Sphere, dentro del mismo fichero, la cual contiene las llamadas a funciones de los ficheros frame.h y vector.h que participan en el bloque. A continuación se muestra un fragmento de Intersect Sphere. 275 int Intersect_Sphere ( RAY * Ray, VECTOR Center, DBL Radius2, DBL * Depth1, DBL * Depth2 ) 276 { 277 DBL OCSquared, t_closest_approach, Half_Chord, t_half_chord_squared ; 278 VECTOR Origin_To_Center ; Increase_Counter ( stats [ Ray_Sphere_Tests ]); VSub ( Origin_To_Center, Center, Ray - > Initial ); VDot ( OCSquared, Origin_To_Center, Origin_To_Center ); VDot ( t_closest_approach, Origin_To_Center, Ray - > Direction ); En la línea 280 está la llamada a la función Increase Counter de frame.h. El compilador hizo inline de esta función. A continuación se muestra la única línea que la compone: frame.h 978 inline void Increase_Counter ( COUNTER & x) 979 { 980 x ++; 981 } Las funciones VSub y VDot de las líneas 282, 284 y 286 son funciones también inline del fichero vector.h. Ambas están sobrecargadas para diferente tipo de parámetros, por lo que todas comparten el mismo contenido. A continuación se muestran un par de ellas a modo de ejemplo:

106 96 CAPÍTULO 8. ESTUDIO EXPERIMENTAL vector.h 87 inline void VSub ( VECTOR a, const VECTOR b, const VECTOR c) 88 { 89 a[ X] = b[ X] - c[ X]; 90 a[ Y] = b[ Y] - c[ Y]; 91 a[ Z] = b[ Z] - c[ Z]; 92 } 221 inline void VDot ( DBL & a, const VECTOR b, const VECTOR c) 222 { 223 a = b[ X] * c[ X] + b[ Y] * c[ Y] + b[ Z] * c[ Z]; 224 } Si echamos un vistazo a la función VSub de la línea 87, vemos que las tres instrucciones que la componen se podrían condensar en un único bucle para invitar al compilador a vectorizarlas con el pragma vector. Además, este fichero contiene multitud de funciones que se rigen bajo el mismo patrón pese a que no participan en el bloque básico del que partió el análisis. El resultado de comprimir las tres operaciones en un bucle sería como se ve en el siguiente código: 87 inline void VSub ( VECTOR a, const VECTOR b, const VECTOR c) 88 { 89 # pragma vector 90 for ( unsigned int i = 0; i < 3; i ++) 91 a[ i] = b[ i] - c[ i]; 92 } Una vez hecho esto se podría volver a compilar y comprobar si el compilador ha vectorizado estos bucles. En caso de que sea así, se podría traducir en un incremento de las instrucciones vectoriales en sustitución de las escalares que minaban el bloque principal Diagnóstico Hardware En esta sección se presentan los experimentos realizados sobre aquellas aplicaciones limitadas por memoria que clasificamos con la denominación memory bound. Cuando se mostraron los resultados de las simulaciones realizadas con CMP$im, había aplicaciones fuertemente limitadas por memoria cuyo análisis fue descartado independientemente de su caracterización vectorial. En un 80 % de los casos se trataba de aplicaciones que presentaban un nivel de vectorización importante, pero debido a las limitaciones de memoria el IPC de la aplicación era deficiente. La siguiente lista indica aquellas que presentaban este perfil: channel y linpk de Polyhedron, Cloverleaf de Mantevo, IRSmk y UMTmk de Sequoia, MG, FT, SP y CG de NPB 470.lbm, 434.zeusmp y 437.leslie3d de SPEC fp. Las pruebas realizadas consistieron en doblar por un lado el tamaño de la cache unificada UL2, y por otro el tamaño del segundo nivel de TLB de datos. Se eligieron estos niveles de memoria porque queríamos visualizar la mejora en caso de que hubiera más aciertos en los niveles de cache previos a la memoria principal. La latencia fue otro de los campos a considerar. Sin embargo, dado

107 8.3. DIAGNÓSTICO HARDWARE 97 que la latencia configurada para la memoria principal era 22x respecto al último nivel de cache, lo descartamos. El objetivo fue por tanto tener una visión general sobre qué ocurriría cuando este tipo de aplicaciones no tienen que visitar tanto la memoria principal Incremento de UL2 Tomamos la decisión de simular solamente las aplicaciones en las que la limitación de memoria superaba un 30 % de los ciclos de toda la aplicación. A la hora de entender las hipotéticas mejoras experimentadas, hubo que tener en cuenta que, en la manipulación de porcentajes, no es lo mismo que se haya experimentado una reducción del 90 % de ciclos de memoria en una aplicación donde suponían un 5 % del total que en otra donde suponían un 50 %. Por otro lado, como nos interesaba ver únicamente las aplicaciones con mejor índice de mejora, en la Figura 8.12 (pág. 97) se visualizaron aquellas cuya mejora del IPC ascendía a más de un 1 %. El orden de las aplicaciones en el eje de abscisas está determinado por la mejora del IPC. Nótese que las mejoras en la memoria se presentan con un porcentaje negativo (menos es mejor). Esto se traduce en un incremento positivo del IPC. En la Tabla 8.14 (pág. 97) se muestran las aplicaciones que teniendo más de 30 % de ciclos de memoria en la aplicación, no han conseguido un mínimo de un 1 % de mejora en el IPC. Figura 8.12: Resultado de doblar la UL2 de 1024Kb a 2048Kb Benchmark Polyhedron Mantevo Sequoia NPB SPEC fp Aplicación linpk nf minimd minighost minife CloverLeaf HPCCG UMTmk IRSmk FT CG IS MG 470.lbm Tabla 8.14: Aplicaciones con una mejora inferior al 1 %

108 98 CAPÍTULO 8. ESTUDIO EXPERIMENTAL La mejora de la aplicación SPEC fp/433.milc, visualizada en detalle en la Figura 8.13 (pág. 98), dobla su IPC. La reducción del 82,2 % de ciclos de memoria ha supuesto una reducción del 52,5 % de ciclos de la aplicación, que se traduce en un 110 % más de IPC. A la derecha se visualiza la distribución del porcentaje de ciclos de la aplicación antes y después. Dejó de ser una aplicación limitada exclusivamente por memoria principal, para pasar a ser una aplicación que, con un 87 % de instrucciones escalares, sería candidata a ser analizada para mejorar el índice de vectorización. Figura 8.13: Mejora de SPEC fp/433.milc al doblar la L2 Ténganse en cuenta casos como NPB/BT donde, pese a haber sufrido un decremento de ciclos de memoria del 71,95 %, no repercute más que un 28,54 % de mejora del IPC. Esto ocurre porque su porcentaje de ciclos de memoria estaba en el límite del 30 % establecido como corte para el experimento. Entonces, aunque se hayan reducido los ciclos de memoria, no se puede reducir más el IPC. Otro caso caso particular es el de polyhedron/test fpu. En la gráfica se aprecia, junto al 25,2 % de mejora de ciclos de memoria, un 4 % en la UL2. Para analizar el resultado tenemos que tener en cuenta los dos conceptos fundamentales sobre: Ciclos de memoria: estos son el resultado de los fallos producidos en la cache unificada de nivel 2 y, debido a la fuerte penalización en ciclos de memoria principal, las instrucciones asociadas siempre aparecerían en el camino crítico. Ciclos de UL2: que estos ciclos formen parte del camino crítico depende de los fallos producidos en la caché de nivel 1. Sin embargo, como la penalización por un acierto en UL2 no es tan grande como la que generaría un fallo, si gracias a haber doblado el tamaño de la UL2 se aumentan sus aciertos, significa que estos ciclos podrían haber dejado de formar parte del camino crítico si la instrucción siguiente dependiera de una load-op anterior de fuerte latencia (ej. división). Véase la Figura 8.14 (pág. 99). Entonces, sabiendo que el número de accesos a la cache es obviamente el mismo y que no hemos doblado el tamaño de la DL1, la reducción del 4 % tiene que estar íntimamente relacionada con que algunas instrucciones que antes formaban parte del camino crítico por fallo en la UL2, ahora con el acierto, ya no lo son.

109 8.3. DIAGNÓSTICO HARDWARE 99 Figura 8.14: Consecuencia posible por aumento de aciertos en L Incremento de TLB Análogamente, no para todas las aplicaciones tenía sentido simular su comportamiento con el doble de líneas en el segundo nivel de TLB. Dado que había muy pocas aplicaciones con muchos ciclos de TLB, se bajó el umbral de selección a un 10 %. No se bajó más porque se considera que en una aplicación con menos del 10 % de ciclos de TLB es irrelevante aplicar una mejora costosa como es aumentar el número de líneas. Incluso así, solo cumplieron el requisito cuatro aplicaciones. Éstas se muestran en la Figura 8.15 (pág. 100) siguiendo la nomenclatura usada anteriormente. La aplicación NPB,IS presenta una mejora del 64,50 % de ciclos que repercuten en una mejora del IPC del 35,10 %. A su lado tenemos a NPB,DC que presenta el mismo comportamiento que veíamos para Polyhedron,test fpu en el apartado anterior. En este caso, además del 93,82 % de mejora de los ciclos de memoria por fallos de TLB, también hay una reducción del 26,37 % en los ciclos de acceso al TLB de segundo nivel. En el modelo, un fallo de TLB que provoca un acceso a la tabla de páginas de la memoria principal acarrea una latencia importante. Si por aumentar el número de líneas se reducen estos accesos, es factible que, como en el caso anterior, las instrucciones que fueran responsables de dichos accesos hayan dejado de formar parte del camino crítico de la aplicación. De todos modos, pese al 93,82 % de mejora de los ciclos, como la reducción se aplica solamente sobre un 13,3 % de ciclos de TLB, al final el IPC solo mejora un 12,5 %. En la Figura 8.16 (pág. 100) se presenta en detalle el resultado de mejorar NPB,IS. En la parte izquierda de la Figura 8.16 (pág. 100), vemos el decremento en ciclos y el aumento de IPC de la aplicación IS de NPB. Pese a que es una mejora tímida, la gráfica derecha nos devuelve a la realidad porque nos recuerda que sigue siendo una aplicación limitada por memoria. Además, si recordamos la Tabla 8.14 (pág. 97), era una de las aplicaciones con una mejora inferior al 1 % cuando se dobló el tamaño de la caché unificada L2. Por tanto, para esta arquitectura en concreto, este tipo de mejoras es insuficiente y no deja otro camino que abrir el paso hacia una posible mejora de la arquitectura en sí como alternativa. El diagnóstico hardware realizado en este apartado quiso marcar el final de la búsqueda del modo de hacer un uso efectivo de la unidad vectorial. Hemos visto aquí que, pese a que el objetivo principal del trabajo se cumpla, el perfil de una aplicación al ser ejecutado sobre una máquina con una configuración de memoria concreta puede impedir que se obtengan las mejoras que se esperarían al hacer un buen uso de la unidad vectorial. En definitiva, la memoria no se puede ignorar y podría ser necesario hacer un buen estudio previo sobre el uso de las estructuras de datos de la aplicación, para, o bien para maximizar el rendimiento de la aplicación sobre la máquina, o bien para mejorar la máquina.

110 100 CAPÍTULO 8. ESTUDIO EXPERIMENTAL Figura 8.15: Resultado de doblar las líneas de DTLB2 de 256 a 512 Figura 8.16: Mejora de IS de NPB al doblar la TLB

Arquitecturas GPU v. 2013

Arquitecturas GPU v. 2013 v. 2013 Stream Processing Similar al concepto de SIMD. Data stream procesado por kernel functions (pipelined) (no control) (local memory, no cache OJO). Data-centric model: adecuado para DSP o GPU (image,

Más detalles

ITT-327-T Microprocesadores

ITT-327-T Microprocesadores ITT-327-T Microprocesadores Introducción al Microprocesador y al Microcomputador. al Microcomputador. Profesor Julio Ferreira. Sistema Microcomputador. Un Sistema Microcomputador tiene dos componentes

Más detalles

4. Programación Paralela

4. Programación Paralela 4. Programación Paralela La necesidad que surge para resolver problemas que requieren tiempo elevado de cómputo origina lo que hoy se conoce como computación paralela. Mediante el uso concurrente de varios

Más detalles

Capítulo 5. Cliente-Servidor.

Capítulo 5. Cliente-Servidor. Capítulo 5. Cliente-Servidor. 5.1 Introducción En este capítulo hablaremos acerca de la arquitectura Cliente-Servidor, ya que para nuestra aplicación utilizamos ésta arquitectura al convertir en un servidor

Más detalles

COMPUTADORES MULTINUCLEO. Stallings W. Computer Organization and Architecture 8ed

COMPUTADORES MULTINUCLEO. Stallings W. Computer Organization and Architecture 8ed COMPUTADORES MULTINUCLEO Stallings W. Computer Organization and Architecture 8ed Computador multinucleo Un computador multinúcleocombina dos o mas procesadores (llamados núcleos) en una única pieza de

Más detalles

DISCOS RAID. Se considera que todos los discos físicos tienen la misma capacidad, y de no ser así, en el que sea mayor se desperdicia la diferencia.

DISCOS RAID. Se considera que todos los discos físicos tienen la misma capacidad, y de no ser así, en el que sea mayor se desperdicia la diferencia. DISCOS RAID Raid: redundant array of independent disks, quiere decir conjunto redundante de discos independientes. Es un sistema de almacenamiento de datos que utiliza varias unidades físicas para guardar

Más detalles

Modelo de aplicaciones CUDA

Modelo de aplicaciones CUDA Modelo de aplicaciones CUDA Utilización de GPGPUs: las placas gráficas se utilizan en el contexto de una CPU: host (CPU) + uno o varios device o GPUs Procesadores masivamente paralelos equipados con muchas

Más detalles

UNIDADES FUNCIONALES DEL ORDENADOR TEMA 3

UNIDADES FUNCIONALES DEL ORDENADOR TEMA 3 UNIDADES FUNCIONALES DEL ORDENADOR TEMA 3 INTRODUCCIÓN El elemento hardware de un sistema básico de proceso de datos se puede estructurar en tres partes claramente diferenciadas en cuanto a sus funciones:

Más detalles

ORDENADORES VECTORIALES

ORDENADORES VECTORIALES ORDENADORES VECTORIALES Un ordenador vectorial es una máquina diseñada específicamente para realizar de forma eficiente operaciones en las que se ven involucrados elementos de matrices, denominados vectores.

Más detalles

LINEAMIENTOS ESTÁNDARES APLICATIVOS DE VIRTUALIZACIÓN

LINEAMIENTOS ESTÁNDARES APLICATIVOS DE VIRTUALIZACIÓN LINEAMIENTOS ESTÁNDARES APLICATIVOS DE VIRTUALIZACIÓN Tabla de Contenidos LINEAMIENTOS ESTÁNDARES APLICATIVOS DE VIRTUALIZACIÓN... 1 Tabla de Contenidos... 1 General... 2 Uso de los Lineamientos Estándares...

Más detalles

Modificación y parametrización del modulo de Solicitudes (Request) en el ERP/CRM Compiere.

Modificación y parametrización del modulo de Solicitudes (Request) en el ERP/CRM Compiere. UNIVERSIDAD DE CARABOBO FACULTAD DE CIENCIA Y TECNOLOGÍA DIRECCION DE EXTENSION COORDINACION DE PASANTIAS Modificación y parametrización del modulo de Solicitudes (Request) en el ERP/CRM Compiere. Pasante:

Más detalles

Introducción En los años 60 s y 70 s cuando se comenzaron a utilizar recursos de tecnología de información, no existía la computación personal, sino que en grandes centros de cómputo se realizaban todas

Más detalles

No se requiere que los discos sean del mismo tamaño ya que el objetivo es solamente adjuntar discos.

No se requiere que los discos sean del mismo tamaño ya que el objetivo es solamente adjuntar discos. RAIDS MODO LINEAL Es un tipo de raid que muestra lógicamente un disco pero se compone de 2 o más discos. Solamente llena el disco 0 y cuando este está lleno sigue con el disco 1 y así sucesivamente. Este

Más detalles

Entorno de Ejecución del Procesador Intel Pentium

Entorno de Ejecución del Procesador Intel Pentium Arquitectura de Ordenadores Arquitectura del Procesador Intel Pentium Abelardo Pardo abel@it.uc3m.es Universidad Carlos III de Madrid Departamento de Ingeniería Telemática Entorno de Ejecución del Procesador

Más detalles

Interoperabilidad de Fieldbus

Interoperabilidad de Fieldbus 2002 Emerson Process Management. Todos los derechos reservados. Vea este y otros cursos en línea en www.plantwebuniversity.com. Fieldbus 201 Interoperabilidad de Fieldbus Generalidades Qué es interoperabilidad?

Más detalles

Introducción a Computación

Introducción a Computación Curso: Modelización y simulación matemática de sistemas Metodología para su implementación computacional Introducción a Computación Esteban E. Mocskos (emocskos@dc.uba.ar) Facultades de Ciencias Exactas

Más detalles

Ciclo de vida y Metodologías para el desarrollo de SW Definición de la metodología

Ciclo de vida y Metodologías para el desarrollo de SW Definición de la metodología Ciclo de vida y Metodologías para el desarrollo de SW Definición de la metodología La metodología para el desarrollo de software es un modo sistemático de realizar, gestionar y administrar un proyecto

Más detalles

INTRODUCCIÓN. Que es un sistema operativo? - Es un programa. - Funciona como intermediario entre el usuario y los programas y el hardware

INTRODUCCIÓN. Que es un sistema operativo? - Es un programa. - Funciona como intermediario entre el usuario y los programas y el hardware INTRODUCCIÓN Que es un sistema operativo? - Es un programa. - Funciona como intermediario entre el usuario y los programas y el hardware INTRODUCCIÓN METAS: Brindar un entorno para que los usuarios puedan

Más detalles

Conclusiones. Particionado Consciente de los Datos

Conclusiones. Particionado Consciente de los Datos Capítulo 6 Conclusiones Una de las principales conclusiones que se extraen de esta tesis es que para que un algoritmo de ordenación sea el más rápido para cualquier conjunto de datos a ordenar, debe ser

Más detalles

ARQUITECTURA DE DISTRIBUCIÓN DE DATOS

ARQUITECTURA DE DISTRIBUCIÓN DE DATOS 4 ARQUITECTURA DE DISTRIBUCIÓN DE DATOS Contenido: Arquitectura de Distribución de Datos 4.1. Transparencia 4.1.1 Transparencia de Localización 4.1.2 Transparencia de Fragmentación 4.1.3 Transparencia

Más detalles

Procesador Intel Core 2 Extreme de 4 núcleos Traducción de Textos Curso 2007/2008

Procesador Intel Core 2 Extreme de 4 núcleos Traducción de Textos Curso 2007/2008 Procesador Intel Core 2 Traducción de Textos Curso 2007/2008 Versión Cambio 0.9RC Revisión del texto 0.8 Traducido el octavo párrafo 0.7 Traducido el séptimo párrafo Autor: Rubén Paje del Pino i010328

Más detalles

UNIVERSIDAD DE SALAMANCA

UNIVERSIDAD DE SALAMANCA UNIVERSIDAD DE SALAMANCA FACULTAD DE CIENCIAS INGENIERÍA TÉCNICA EN INFORMÁTICA DE SISTEMAS Resumen del trabajo práctico realizado para la superación de la asignatura Proyecto Fin de Carrera. TÍTULO SISTEMA

Más detalles

UNIVERSIDAD TECNOLOGICA ECOTEC DIEGO BARRAGAN MATERIA: Sistemas Operativos 1 ENSAYO: Servidores BLADE

UNIVERSIDAD TECNOLOGICA ECOTEC DIEGO BARRAGAN MATERIA: Sistemas Operativos 1 ENSAYO: Servidores BLADE UNIVERSIDAD TECNOLOGICA ECOTEC DIEGO BARRAGAN MATERIA: Sistemas Operativos 1 ENSAYO: Servidores BLADE AÑO: 2010 Qué es un servidor Blade? Blade Server es una arquitectura que ha conseguido integrar en

Más detalles

Tema: Historia de los Microprocesadores

Tema: Historia de los Microprocesadores Universidad Nacional de Ingeniería Arquitectura de Maquinas I Unidad I: Introducción a los Microprocesadores y Microcontroladores. Tema: Historia de los Microprocesadores 1 Contenidos La década de los

Más detalles

Gestión de la Configuración

Gestión de la Configuración Gestión de la ÍNDICE DESCRIPCIÓN Y OBJETIVOS... 1 ESTUDIO DE VIABILIDAD DEL SISTEMA... 2 ACTIVIDAD EVS-GC 1: DEFINICIÓN DE LOS REQUISITOS DE GESTIÓN DE CONFIGURACIÓN... 2 Tarea EVS-GC 1.1: Definición de

Más detalles

HISTORIA Y EVOLUCIÓN DE LOS SISTEMAS OPERATIVOS

HISTORIA Y EVOLUCIÓN DE LOS SISTEMAS OPERATIVOS HISTORIA Y EVOLUCIÓN DE LOS SISTEMAS OPERATIVOS Las primeras computadoras eran enormes máquinas que se ejecutaban desde una consola. El programador, quien además operaba el sistema de computación, debía

Más detalles

CAPÍTULO 2 Sistemas De Base De Datos Multiusuarios

CAPÍTULO 2 Sistemas De Base De Datos Multiusuarios CAPÍTULO 2 Sistemas De De Multiusuarios Un sistema multiusuario es un sistema informático que da servicio, manera concurrente, a diferentes usuarios mediante la utilización compartida sus recursos. Con

Más detalles

Análisis de aplicación: Virtual Machine Manager

Análisis de aplicación: Virtual Machine Manager Análisis de aplicación: Virtual Machine Manager Este documento ha sido elaborado por el Centro de Apoyo Tecnológico a Emprendedores bilib, www.bilib.es Copyright 2011, Junta de Comunidades de Castilla

Más detalles

PRACTICA 2 Ejercicio 3

PRACTICA 2 Ejercicio 3 PRACTICA 2 Ejercicio 3 Análisis de la Memoria Cache L1 de Datos para Procesadores Superescalares con Ejecución uera de Orden DESCRIPCIÓN GENERAL El objetivo de este ejercicio práctico consiste en obtener

Más detalles

picojava TM Características

picojava TM Características picojava TM Introducción El principal objetivo de Sun al introducir Java era poder intercambiar programas ejecutables Java entre computadoras de Internet y ejecutarlos sin modificación. Para poder transportar

Más detalles

Introducción a las redes de computadores

Introducción a las redes de computadores Introducción a las redes de computadores Contenido Descripción general 1 Beneficios de las redes 2 Papel de los equipos en una red 3 Tipos de redes 5 Sistemas operativos de red 7 Introducción a las redes

Más detalles

Arquitectura de sistema de alta disponibilidad

Arquitectura de sistema de alta disponibilidad Mysql Introducción MySQL Cluster esta diseñado para tener una arquitectura distribuida de nodos sin punto único de fallo. MySQL Cluster consiste en 3 tipos de nodos: 1. Nodos de almacenamiento, son los

Más detalles

Guía de uso del Cloud Datacenter de acens

Guía de uso del Cloud Datacenter de acens guíasdeuso Guía de uso del Cloud Datacenter de Calle San Rafael, 14 28108 Alcobendas (Madrid) 902 90 10 20 www..com Introducción Un Data Center o centro de datos físico es un espacio utilizado para alojar

Más detalles

Procesador Pentium II 450 MHz Procesador Pentium II 400 MHz Procesador Pentium II 350 MHz Procesador Pentium II 333 MHz Procesador Pentium II 300 MHz

Procesador Pentium II 450 MHz Procesador Pentium II 400 MHz Procesador Pentium II 350 MHz Procesador Pentium II 333 MHz Procesador Pentium II 300 MHz PENTIUM El procesador Pentium es un miembro de la familia Intel de procesadores de propósito general de 32 bits. Al igual que los miembros de esta familia, el 386 y el 486, su rango de direcciones es de

Más detalles

18. Camino de datos y unidad de control

18. Camino de datos y unidad de control Oliverio J. Santana Jaria Sistemas Digitales Ingeniería Técnica en Informática de Sistemas Curso 2006 2007 18. Camino de datos y unidad de control Un La versatilidad una característica deseable los Los

Más detalles

1.2 Análisis de los Componentes. Arquitectura de Computadoras Rafael Vazquez Perez

1.2 Análisis de los Componentes. Arquitectura de Computadoras Rafael Vazquez Perez 1.2 Análisis de los Componentes. Arquitectura de Computadoras Rafael Vazquez Perez 1.2.1 CPU 1 Arquitecturas. 2 Tipos. 3 Características. 4 Funcionamiento(ALU, unidad de control, Registros y buses internos)

Más detalles

SCT3000 95. Software para la calibración de transductores de fuerza. Versión 3.5. Microtest S.A. microtes@arrakis.es

SCT3000 95. Software para la calibración de transductores de fuerza. Versión 3.5. Microtest S.A. microtes@arrakis.es SCT3000 95 Versión 3.5 Software para la calibración de transductores de fuerza. Microtest S.A. microtes@arrakis.es Introducción El programa SCT3000 95, es un sistema diseñado para la calibración automática

Más detalles

RESUMEN CUADRO DE MANDO

RESUMEN CUADRO DE MANDO 1. Objetivo Los objetivos que pueden alcanzarse, son: RESUMEN CUADRO DE MANDO Disponer eficientemente de la información indispensable y significativa, de modo sintético, conectada con los objetivos. Facilitar

Más detalles

FUNDAMENTOS DE COMPUTACIÓN PARA CIENTÍFICOS. CNCA Abril 2013

FUNDAMENTOS DE COMPUTACIÓN PARA CIENTÍFICOS. CNCA Abril 2013 FUNDAMENTOS DE COMPUTACIÓN PARA CIENTÍFICOS CNCA Abril 2013 6. COMPUTACIÓN DE ALTO RENDIMIENTO Ricardo Román DEFINICIÓN High Performance Computing - Computación de Alto Rendimiento Técnicas, investigación

Más detalles

Tema 1 Introducción. Arquitectura básica y Sistemas Operativos. Fundamentos de Informática

Tema 1 Introducción. Arquitectura básica y Sistemas Operativos. Fundamentos de Informática Tema 1 Introducción. Arquitectura básica y Sistemas Operativos Fundamentos de Informática Índice Descripción de un ordenador Concepto básico de Sistema Operativo Codificación de la información 2 1 Descripción

Más detalles

TEMA 4. Unidades Funcionales del Computador

TEMA 4. Unidades Funcionales del Computador TEMA 4 Unidades Funcionales del Computador Álvarez, S., Bravo, S., Departamento de Informática y automática Universidad de Salamanca Introducción El elemento físico, electrónico o hardware de un sistema

Más detalles

Evaluación del rendimiento de procesadores Intel Nehalem. Modelos x7550, x5670 y x5570

Evaluación del rendimiento de procesadores Intel Nehalem. Modelos x7550, x5670 y x5570 Evaluación del rendimiento de procesadores Intel Nehalem. Modelos x7550, x5670 y x5570 Juan Carlos Fernández Rodríguez. Área de HPC. Centro Informático Científico de Andalucía (CICA) Junta de Andalucía

Más detalles

2.1 Clasificación de los sistemas de Producción.

2.1 Clasificación de los sistemas de Producción. ADMINISTRACION DE OPERACIONES Sesión 2: La Administración de operaciones II Objetivo específico 1: El alumno conocerá la clasificación de los sistemas de producción, los sistemas avanzados de manufactura

Más detalles

2 EL DOCUMENTO DE ESPECIFICACIONES

2 EL DOCUMENTO DE ESPECIFICACIONES Ingeniería Informática Tecnología de la Programación TEMA 1 Documentación de programas. 1 LA DOCUMENTACIÓN DE PROGRAMAS En la ejecución de un proyecto informático o un programa software se deben de seguir

Más detalles

CRONO SISTEMA DE CONTROL DE PRESENCIA. Software abierto. Distintas opciones para realizar las picadas. Web personal para cada usuario

CRONO SISTEMA DE CONTROL DE PRESENCIA. Software abierto. Distintas opciones para realizar las picadas. Web personal para cada usuario Software abierto Distintas opciones para realizar las picadas Web personal para cada usuario Gestión de incidencias Informes individuales y colectivos CRONO SISTEMA DE CONTROL DE PRESENCIA Qué es Crono?

Más detalles

Capítulo 4. Requisitos del modelo para la mejora de la calidad de código fuente

Capítulo 4. Requisitos del modelo para la mejora de la calidad de código fuente Capítulo 4. Requisitos del modelo para la mejora de la calidad de código fuente En este capítulo definimos los requisitos del modelo para un sistema centrado en la mejora de la calidad del código fuente.

Más detalles

WINDOWS 2008 7: COPIAS DE SEGURIDAD

WINDOWS 2008 7: COPIAS DE SEGURIDAD 1.- INTRODUCCION: WINDOWS 2008 7: COPIAS DE SEGURIDAD Las copias de seguridad son un elemento fundamental para que el trabajo que realizamos se pueda proteger de aquellos problemas o desastres que pueden

Más detalles

Nicolás Zarco Arquitectura Avanzada 2 Cuatrimestre 2011

Nicolás Zarco Arquitectura Avanzada 2 Cuatrimestre 2011 Clusters Nicolás Zarco Arquitectura Avanzada 2 Cuatrimestre 2011 Introducción Aplicaciones que requieren: Grandes capacidades de cómputo: Física de partículas, aerodinámica, genómica, etc. Tradicionalmente

Más detalles

Un primer acercamiento a la CMDB.

Un primer acercamiento a la CMDB. Un Versión primer 1.2 acercamiento a la CMDB. 20/07/2005 Un primer acercamiento a la CMDB. Versión 1.1 1.2 18/02/05 20/02/05 Fecha Jose Autores Carlos Manuel García Viejo García Lobato http://ars.viejolobato.com

Más detalles

Técnicas empleadas. además de los discos las controladoras.

Técnicas empleadas. además de los discos las controladoras. RAID Introducción En los últimos años, la mejora en la tecnología de semiconductores ha significado un gran incremento en la velocidad de los procesadores y las memorias principales que, a su vez, exigen

Más detalles

Sistemas de Información Geográficos (SIG o GIS)

Sistemas de Información Geográficos (SIG o GIS) Sistemas de Información Geográficos (SIG o GIS) 1) Qué es un SIG GIS? 2) Para qué sirven? 3) Tipos de datos 4) Cómo trabaja? 5) Modelos de datos, Diseño Conceptual 6) GeoDataase (GD) 7) Cómo evaluamos

Más detalles

Estructura de Computadores I Arquitectura de los MMOFPS

Estructura de Computadores I Arquitectura de los MMOFPS UNIVERSIDAD TÉCNICA FEDERICO SANTA MARÍA Estructura de Computadores I Arquitectura de los MMOFPS Integrantes: Luis Castro Valentina Yévenes RESUMEN Los MMOG (Massively Multiplayer Online Game), son juegos

Más detalles

CAPÍTULO 2 DEFINICIÓN DEL PROBLEMA

CAPÍTULO 2 DEFINICIÓN DEL PROBLEMA CAPÍTULO 2 DEFINICIÓN DEL PROBLEMA En el capítulo anterior se describió la situación inicial en la que se encontraba la Coordinación de Cómputo Académico (CCA) del Departamento de Ingenierías (DI) de la

Más detalles

Servicios avanzados de supercomputación para la ciència y la ingeniería

Servicios avanzados de supercomputación para la ciència y la ingeniería Servicios avanzados de supercomputación para la ciència y la ingeniería Servicios avanzados de supercomputación para la ciència y la ingeniería HPCNow! provee a sus clientes de la tecnología y soluciones

Más detalles

App para realizar consultas al Sistema de Información Estadística de Castilla y León

App para realizar consultas al Sistema de Información Estadística de Castilla y León App para realizar consultas al Sistema de Información Estadística de Castilla y León Jesús M. Rodríguez Rodríguez rodrodje@jcyl.es Dirección General de Presupuestos y Estadística Consejería de Hacienda

Más detalles

Sesión No. 4. Contextualización INFORMÁTICA 1. Nombre: Procesador de Texto

Sesión No. 4. Contextualización INFORMÁTICA 1. Nombre: Procesador de Texto INFORMÁTICA INFORMÁTICA 1 Sesión No. 4 Nombre: Procesador de Texto Contextualización La semana anterior revisamos los comandos que ofrece Word para el formato del texto, la configuración de la página,

Más detalles

Ahorro de energía visualizando páginas Web en dispositivos móviles heterogéneos

Ahorro de energía visualizando páginas Web en dispositivos móviles heterogéneos ROC&C 06 Ahorro de energía visualizando páginas Web en dispositivos móviles heterogéneos Dr. Juan Gabriel González Serna. M.C. Juan Carlos Olivares Rojas. Acapulco, Guerrero, México, 2006. Agenda Introducción

Más detalles

CURSO: APACHE SPARK CAPÍTULO 2: INTRODUCCIÓN A APACHE SPARK. www.formacionhadoop.com

CURSO: APACHE SPARK CAPÍTULO 2: INTRODUCCIÓN A APACHE SPARK. www.formacionhadoop.com CURSO: APACHE SPARK CAPÍTULO 2: INTRODUCCIÓN A APACHE SPARK www.formacionhadoop.com Índice 1 Qué es Big Data? 2 Problemas con los sistemas tradicionales 3 Qué es Spark? 3.1 Procesamiento de datos distribuido

Más detalles

Sistema de SaaS (Software as a Service) para centros educativos

Sistema de SaaS (Software as a Service) para centros educativos Sistema de SaaS (Software as a Service) para centros educativos Definiciones preliminares: Qué es SaaS? SaaS (1) es un modelo de distribución del software que permite a los usuarios el acceso al mismo

Más detalles

INTRANET DE UNA EMPRESA RESUMEN DEL PROYECTO. PALABRAS CLAVE: Aplicación cliente-servidor, Intranet, Área reservada, Red INTRODUCCIÓN

INTRANET DE UNA EMPRESA RESUMEN DEL PROYECTO. PALABRAS CLAVE: Aplicación cliente-servidor, Intranet, Área reservada, Red INTRODUCCIÓN INTRANET DE UNA EMPRESA Autor: Burgos González, Sergio. Director: Zaforas de Cabo, Juan. Entidad colaboradora: Colegio de Ingenieros del ICAI. RESUMEN DEL PROYECTO El proyecto consiste en el desarrollo

Más detalles

IV. Implantación del sistema.

IV. Implantación del sistema. IV. Implantación del sistema. Para hablar sobre el proceso de desarrollo del sistema de Recuperación de Información Visual propuesto, empezaremos hablando del hardware utilizado, las herramientas de software

Más detalles

Parámetros con la ventana de selección de usuario, reglas, texto y descomposición (IVE)

Parámetros con la ventana de selección de usuario, reglas, texto y descomposición (IVE) QUÉ SON CONCEPTOS PARAMÉTRICOS? Los conceptos paramétricos de Presto permiten definir de una sola vez una colección de conceptos similares a partir de los cuales se generan variantes o conceptos derivados

Más detalles

Figura 1. Símbolo que representa una ALU. El sentido y la funcionalidad de las señales de la ALU de la Figura 1 es el siguiente:

Figura 1. Símbolo que representa una ALU. El sentido y la funcionalidad de las señales de la ALU de la Figura 1 es el siguiente: Departamento de Ingeniería de Sistemas Facultad de Ingeniería Universidad de Antioquia Arquitectura de Computadores y Laboratorio ISI355 (2011 2) Práctica No. 1 Diseño e implementación de una unidad aritmético

Más detalles

Mejores prácticas para el éxito de un sistema de información. Uno de los problemas de información dentro de las empresas es contar con datos

Mejores prácticas para el éxito de un sistema de información. Uno de los problemas de información dentro de las empresas es contar con datos ANEXO VI. Mejores prácticas para el éxito de un sistema de información Uno de los problemas de información dentro de las empresas es contar con datos importantes del negocio y que éstos estén aislados

Más detalles

Propuesta de Portal de la Red de Laboratorios Virtuales y Remotos de CEA

Propuesta de Portal de la Red de Laboratorios Virtuales y Remotos de CEA Propuesta de Portal de la Red de Laboratorios Virtuales y Remotos de CEA Documento de trabajo elaborado para la Red Temática DocenWeb: Red Temática de Docencia en Control mediante Web (DPI2002-11505-E)

Más detalles

INGENIERÍA DE SOFTWARE. Sesión 3: Tipos

INGENIERÍA DE SOFTWARE. Sesión 3: Tipos INGENIERÍA DE SOFTWARE Sesión 3: Tipos Contextualización Actualmente existe una gran variedad en los software que se pueden clasificar en varias categorías, como pueden ser, por tipo de licencia, tipo

Más detalles

Metodologías de diseño de hardware

Metodologías de diseño de hardware Capítulo 2 Metodologías de diseño de hardware Las metodologías de diseño de hardware denominadas Top-Down, basadas en la utilización de lenguajes de descripción de hardware, han posibilitado la reducción

Más detalles

Elementos requeridos para crearlos (ejemplo: el compilador)

Elementos requeridos para crearlos (ejemplo: el compilador) Generalidades A lo largo del ciclo de vida del proceso de software, los productos de software evolucionan. Desde la concepción del producto y la captura de requisitos inicial hasta la puesta en producción

Más detalles

DISEÑO DE FUNCIONES (TRATAMIENTOS)

DISEÑO DE FUNCIONES (TRATAMIENTOS) DISEÑO DE FUNCIONES (TRATAMIENTOS) Diseño Estructurado. Estrategias para Derivar el Diagrama de Estructura. Diseño de Módulos Programables. 1. DISEÑO ESTRUCTURADO El Diseño es el proceso por el cual se

Más detalles

CAPÍTULO 7 7. CONCLUSIONES

CAPÍTULO 7 7. CONCLUSIONES CAPÍTULO 7 7. CONCLUSIONES 7.1. INTRODUCCIÓN 7.2. CONCLUSIONES PARTICULARES 7.3. CONCLUSIONES GENERALES 7.4. APORTACIONES DEL TRABAJO DE TESIS 7.5. PROPUESTA DE TRABAJOS FUTUROS 197 CAPÍTULO 7 7. Conclusiones

Más detalles

Hostaliawhitepapers. Las ventajas de los Servidores dedicados. www.hostalia.com. Cardenal Gardoki, 1 48008 BILBAO (Vizcaya) Teléfono: 902 012 199

Hostaliawhitepapers. Las ventajas de los Servidores dedicados. www.hostalia.com. Cardenal Gardoki, 1 48008 BILBAO (Vizcaya) Teléfono: 902 012 199 Las ventajas de los Servidores dedicados Cardenal Gardoki, 1 48008 BILBAO (Vizcaya) Teléfono: 902 012 199 www.hostalia.com A la hora de poner en marcha una aplicación web debemos contratar un servicio

Más detalles

Unidad 1. Fundamentos en Gestión de Riesgos

Unidad 1. Fundamentos en Gestión de Riesgos 1.1 Gestión de Proyectos Unidad 1. Fundamentos en Gestión de Riesgos La gestión de proyectos es una disciplina con la cual se integran los procesos propios de la gerencia o administración de proyectos.

Más detalles

"Diseño, construcción e implementación de modelos matemáticos para el control automatizado de inventarios

Diseño, construcción e implementación de modelos matemáticos para el control automatizado de inventarios "Diseño, construcción e implementación de modelos matemáticos para el control automatizado de inventarios Miguel Alfonso Flores Sánchez 1, Fernando Sandoya Sanchez 2 Resumen En el presente artículo se

Más detalles

Mineria de datos y su aplicación en web mining data Redes de computadores I ELO 322

Mineria de datos y su aplicación en web mining data Redes de computadores I ELO 322 Mineria de datos y su aplicación en web mining data Redes de computadores I ELO 322 Nicole García Gómez 2830047-6 Diego Riquelme Adriasola 2621044-5 RESUMEN.- La minería de datos corresponde a la extracción

Más detalles

Capítulo 12: Indexación y asociación

Capítulo 12: Indexación y asociación Capítulo 12: Indexación y asociación Conceptos básicos Índices ordenados Archivos de índice de árbol B+ Archivos de índice de árbol B Asociación estática Asociación dinámica Comparación entre indexación

Más detalles

PCLabs. Práctica de Ingeniería del Software I Curso 2008/09. Descripción del problema

PCLabs. Práctica de Ingeniería del Software I Curso 2008/09. Descripción del problema PCLabs Práctica de Ingeniería del Software I Curso 2008/09 Descripción del problema Introducción... 1 Organización de la empresa... 1 Gestión del almacén... 2 Gestión de pedidos de clientes... 3 Gestión

Más detalles

Sistema de marketing de proximidad

Sistema de marketing de proximidad Dizan Vasquez Propuesta de proyecto Sistema de marketing de proximidad ACME México Dizan Vasquez Índice general 1. Descripción 3 2. Resúmen ejecutivo 4 2.1. Objetivo.................................................

Más detalles

PROGRAMACIÓN ORIENTADA A OBJETOS Master de Computación. II MODELOS y HERRAMIENTAS UML. II.2 UML: Modelado de casos de uso

PROGRAMACIÓN ORIENTADA A OBJETOS Master de Computación. II MODELOS y HERRAMIENTAS UML. II.2 UML: Modelado de casos de uso PROGRAMACIÓN ORIENTADA A OBJETOS Master de Computación II MODELOS y HERRAMIENTAS UML 1 1 Modelado de casos de uso (I) Un caso de uso es una técnica de modelado usada para describir lo que debería hacer

Más detalles

LOGISTICA D E COMPRAS

LOGISTICA D E COMPRAS LOGISTICA D E COMPRAS 1. - Concepto de compras OBTENER EL (LOS) PRODUCTO(S) O SERVICIO(S) DE LA CALIDAD ADECUADA, CON EL PRECIO JUSTO, EN EL TIEMPO INDICADO Y EN EL LUGAR PRECISO. Muchas empresas manejan

Más detalles

INFORMÁTICA. Matemáticas aplicadas a la Informática

INFORMÁTICA. Matemáticas aplicadas a la Informática ACCESO A CICLO SUPERIOR INFORMÁTICA Matemáticas aplicadas a la Informática http://trasteandoencontre.km6.net/ 1 Acceso a grado Superior. Informática 1. Unidades de medida en informática Como sabemos, el

Más detalles

TIPOS DE PROCESAMIENTOS

TIPOS DE PROCESAMIENTOS TIPOS DE PROCESAMIENTOS El desempeño de un computador puede tener diferentes medidas de elección para diferentes usuarios. Para un usuario individual que está ejecutando un único programa, la computadora

Más detalles

K2BIM Plan de Investigación - Comparación de herramientas para la parametrización asistida de ERP Versión 1.2

K2BIM Plan de Investigación - Comparación de herramientas para la parametrización asistida de ERP Versión 1.2 K2BIM Plan de Investigación - Comparación de herramientas para la parametrización asistida de ERP Versión 1.2 Historia de revisiones Fecha VersiónDescripción Autor 08/10/2009 1.0 Creación del documento.

Más detalles

ÍNDICE DISEÑO DE CONTADORES SÍNCRONOS JESÚS PIZARRO PELÁEZ

ÍNDICE DISEÑO DE CONTADORES SÍNCRONOS JESÚS PIZARRO PELÁEZ ELECTRÓNICA DIGITAL DISEÑO DE CONTADORES SÍNCRONOS JESÚS PIZARRO PELÁEZ IES TRINIDAD ARROYO DPTO. DE ELECTRÓNICA ÍNDICE ÍNDICE... 1 1. LIMITACIONES DE LOS CONTADORES ASÍNCRONOS... 2 2. CONTADORES SÍNCRONOS...

Más detalles

CAPÍTULO 1 Instrumentación Virtual

CAPÍTULO 1 Instrumentación Virtual CAPÍTULO 1 Instrumentación Virtual 1.1 Qué es Instrumentación Virtual? En las últimas décadas se han incrementado de manera considerable las aplicaciones que corren a través de redes debido al surgimiento

Más detalles

CRIPTOGRAFÍA SIMÉTRICA Y ASIMÉTRICA

CRIPTOGRAFÍA SIMÉTRICA Y ASIMÉTRICA CRIPTOGRAFÍA SIMÉTRICA Y ASIMÉTRICA Para generar una transmisión segura de datos, debemos contar con un canal que sea seguro, esto es debemos emplear técnicas de forma que los datos que se envían de una

Más detalles

Tutorial: Primeros Pasos con Subversion

Tutorial: Primeros Pasos con Subversion Tutorial: Primeros Pasos con Subversion Introducción Subversion es un sistema de control de versiones open source. Corre en distintos sistemas operativos y su principal interfaz con el usuario es a través

Más detalles

UNIDADES DE ALMACENAMIENTO DE DATOS

UNIDADES DE ALMACENAMIENTO DE DATOS 1.2 MATÉMATICAS DE REDES 1.2.1 REPRESENTACIÓN BINARIA DE DATOS Los computadores manipulan y almacenan los datos usando interruptores electrónicos que están ENCENDIDOS o APAGADOS. Los computadores sólo

Más detalles

El ABC de Big Data: Analytics, Bandwidth and Content

El ABC de Big Data: Analytics, Bandwidth and Content Documento técnico El ABC de Big Data: Analytics, Bandwidth and Content Richard Treadway e Ingo Fuchs, NetApp, Noviembre de 2011 WP-7147 RESUMEN EJECUTIVO Las empresas entran en una nueva era en la que

Más detalles

Servicio de Alta, Baja, Modificación y Consulta de usuarios Medusa

Servicio de Alta, Baja, Modificación y Consulta de usuarios Medusa Documentos de Proyecto Medusa Documentos de: Serie: Manuales Servicio de Alta, Baja, Modificación y Consulta del documento: Fecha 22 de febrero de 2007 Preparado por: José Ramón González Luis Aprobado

Más detalles

M.T.I. Arturo López Saldiña

M.T.I. Arturo López Saldiña M.T.I. Arturo López Saldiña Hoy en día, existen diversas aproximaciones al tema de cómo hacer que las personas trabajen dentro de una organización de manera colaborativa. El problema se vuelve más difícil

Más detalles

Unidad I. 1.1 Sistemas numéricos (Binario, Octal, Decimal, Hexadecimal)

Unidad I. 1.1 Sistemas numéricos (Binario, Octal, Decimal, Hexadecimal) Unidad I Sistemas numéricos 1.1 Sistemas numéricos (Binario, Octal, Decimal, Hexadecimal) Los computadores manipulan y almacenan los datos usando interruptores electrónicos que están ENCENDIDOS o APAGADOS.

Más detalles

ADT CONSULTING S.L. http://www.adtconsulting.es PROYECTO DE DIFUSIÓN DE BUENAS PRÁCTICAS

ADT CONSULTING S.L. http://www.adtconsulting.es PROYECTO DE DIFUSIÓN DE BUENAS PRÁCTICAS ADT CONSULTING S.L. http://www.adtconsulting.es PROYECTO DE DIFUSIÓN DE BUENAS PRÁCTICAS ESTUDIO SOBRE EL POSICIONAMIENTO EN BUSCADORES DE PÁGINAS WEB Y LA RELEVANCIA DE LA ACTUALIZACIÓN DE CONTENIDOS

Más detalles

Diseño orientado al flujo de datos

Diseño orientado al flujo de datos Diseño orientado al flujo de datos Recordemos que el diseño es una actividad que consta de una serie de pasos, en los que partiendo de la especificación del sistema (de los propios requerimientos), obtenemos

Más detalles

Creado dentro de la línea de sistemas operativos producida por Microsoft Corporation.

Creado dentro de la línea de sistemas operativos producida por Microsoft Corporation. WINDOWS Windows, Es un Sistema Operativo. Creado dentro de la línea de sistemas operativos producida por Microsoft Corporation. Dentro de los tipos de Software es un tipo de software de Sistemas. Windows

Más detalles

Ingº CIP Fabian Guerrero Medina Master Web Developer-MWD

Ingº CIP Fabian Guerrero Medina Master Web Developer-MWD 1 Java es un lenguaje de programación de Sun Microsystems originalmente llamado "Oak. James Gosling Bill Joy 2 Oak nació para programar pequeños dispositivos electrodomésticos, como los asistentes personales

Más detalles

Introducción a la Firma Electrónica en MIDAS

Introducción a la Firma Electrónica en MIDAS Introducción a la Firma Electrónica en MIDAS Firma Digital Introducción. El Módulo para la Integración de Documentos y Acceso a los Sistemas(MIDAS) emplea la firma digital como método de aseguramiento

Más detalles

1.4.1.2. Resumen... 1.4.2. ÁREA DE FACTURACIÓN::INFORMES::Pedidos...27 1.4.2.1. Detalle... 1.4.2.2. Resumen... 1.4.3. ÁREA DE

1.4.1.2. Resumen... 1.4.2. ÁREA DE FACTURACIÓN::INFORMES::Pedidos...27 1.4.2.1. Detalle... 1.4.2.2. Resumen... 1.4.3. ÁREA DE MANUAL DE USUARIO DE ABANQ 1 Índice de contenido 1 ÁREA DE FACTURACIÓN......4 1.1 ÁREA DE FACTURACIÓN::PRINCIPAL...4 1.1.1. ÁREA DE FACTURACIÓN::PRINCIPAL::EMPRESA...4 1.1.1.1. ÁREA DE FACTURACIÓN::PRINCIPAL::EMPRESA::General...4

Más detalles

Guía de selección de hardware Windows MultiPoint Server 2010

Guía de selección de hardware Windows MultiPoint Server 2010 Guía de selección de hardware Windows MultiPoint Server 2010 Versión de documento 1.0 Publicado en marzo del 2010 Información sobre los derechos de reproducción Este documento se proporciona como está.

Más detalles

Capítulo V Resultados y conclusiones

Capítulo V Resultados y conclusiones Capítulo V Resultados y conclusiones Nadav Levanon, autor del libro Radar Principles dijo: el estudio de los radares no solo una aplicación práctica, pero también una disciplina científica madura con fundamentos

Más detalles

TABLA DE DECISION. Consideremos la siguiente tabla, expresada en forma genérica, como ejemplo y establezcamos la manera en que debe leerse.

TABLA DE DECISION. Consideremos la siguiente tabla, expresada en forma genérica, como ejemplo y establezcamos la manera en que debe leerse. TABLA DE DECISION La tabla de decisión es una herramienta que sintetiza procesos en los cuales se dan un conjunto de condiciones y un conjunto de acciones a tomar según el valor que toman las condiciones.

Más detalles