UNIVERSIDAD DE EXTREMADURA Escuela Politécnica MÁSTER UNIVERSITARIO EN COMPUTACIÓN GRID Y PARALELISMO Trabajo Fin de Máster Implementación paralela del algoritmo Pixel Purity Index para clúster de GPUs José Miguel Franco Valiente Septiembre, 2012 . . . UNIVERSIDAD DE EXTREMADURA Escuela Politécnica MÁSTER UNIVERSITARIO EN COMPUTACIÓN GRID Y PARALELISMO Trabajo Fin de Máster Implementación paralela del algoritmo Pixel Purity Index para clúster de GPUs Autor: Fdo: Director: Fdo: José Miguel Franco Valiente Antonio J. Plaza Miguel Tribunal calificador Presidente: Fdo: Secretario: Fdo: Vocal: Fdo: CALIFICACIÓN FECHA vi Agradecimientos Quiero manifestar mi agradecimiento a todas las personas que, directa o indirectamente han contribuido al resultado de este trabajo. En especial: A Marı́a de los Ángeles, por su paciencia infinita, su gran ayuda y por sus ánimos recibidos durante la realización de este trabajo. A mi familia, por su apoyo incondicional y su confianza depositada en mi para poder llevar este trabajo a buen puerto. A Antonio Plaza y al grupo Hypercomp, por iniciarme en la lı́nea de investigación en el análisis de datos hiperespectrales, por su buen trato recibido y su soporte y apoyo en las dudas que han surgido durante la realización de este trabajo. A CETA-CIEMAT, por permitirme el uso de sus recursos computacionales para la realización de este trabajo y especialmente a su personal por dedicarme su tiempo para resolver mis dudas y responder eficazmente a las incidencias detectadas. vii viii Resumen Desde hace unos años, el uso de las tarjetas gráficas (GPUs) para acelerar cálculos cientı́ficos se ha hecho muy popular, debido principalmente a la relación precio - rendimiento que se puede alcanzar con este tipo de dispositivos y al no necesitar éstos de grandes espacios ni instalaciones especiales para poder trabajar con ellos. Además, este tipo de dispositivos está siendo integrados en los grandes computadores, aumentando de manera notable sus capacidades de cómputo y permitiendo que arquitecturas hı́bridas de CPUs y GPUs aparezcan en los primeros puestos del ránking de supercomputadores Top500. Además, la aparición de los proveedores de computación en la nube o Cloud Computing como Amazon permite el acceso a cualquier usuario de a pie a recursos de computación de alto rendimiento, pagando solamente por el uso que se le dé a dichos recursos. Ası́ pues, es posible configurar un clúster de computadores con CPUs de alto rendimiento y tarjetas GPU integradas en cuestión de segundos si se tiene un conocimiento básico de las APIs de los proveedores de cloud. En concreto, en la lı́nea de investigación en el análisis de imágenes hiperespectrales para la observación remota de la Tierra, las GPUs están permitiendo disminuir el tiempo de procesamiento de este tipo de imágenes, algo fundamental debido al campo de aplicación de esta disciplina que comprende actividades como son la detección de incendios, detección de objetivos militares y el control de vertidos en la atmósfera o en aguas entre otras. Uno de los algoritmos más conocidos para la extracción de firmas espectrales puras o endmembers en imágenes multiespectrales o hiperespectrales es el algoritmo Pixel Purity Index o PPI. Este algoritmo es de gran relevancia debido a que las imágenes hiperespectrales capturadas por los sensores actuales de observación remota de la Tierra almacenan en cada pı́xel de resolución espacial una mezcla de sustancias puras. Esto quiere decir que un pı́xel representa una superficie, que suele ser mı́nimo del orden del metro cuadrado, donde puede aparecer vegetación, agua, edificaciones, etcétera. El algoritmo PPI se incluye en el paquete comercial ENVI, perteneciente a la compañı́a Exelis Visual Information Solutions. El núcleo de este trabajo reside en la implementación de una versión eficiente del algoritmo PPI para clúster de computadores cuyos nodos de cálculo integran tarjetas GPU (o clúster de GPUs). Para la implementación y ejecución de las pruebas se ha utilizado el clúster de GPUs de CETACIEMAT, localizado en Trujillo (Cáceres), que consta de 17 nodos de computación para el entorno de Test equipados con GPUs C1060 (S1070) de Nvidia y 32 nodos de computación para el entorno de Producción equipados con GPUs de la serie C2050 (S2050) y C2070 (S2070) de Nvidia. La implementación desarrollada de PPI se basa en las implementaciones de este algoritmo desarrolladas previamente para clúster de computadores y para tarjetas GPU. Por este motivo, este trabajo presenta un estudio comparativo de rendimiento entre las tres implementaciones paralelas comentadas. Asimismo, se presenta un estudio de las diferentes implementaciones llevadas a cabo en el marco de este trabajo con el fin de presentar la evolución del rendimiento desde la primera versión del algoritmo hasta la última, la cual incluye varias optimizaciones (entre ellas el uso de varias GPUs). Palabras clave Hiperespectral, Endmember, Pixel Purity Index, PPI, multiGPU, ENVI. ix x Abstract In recent years, the use of graphic cards (GPUs) to accelerate scientific calculations has become very popular, mainly because of the price - performance ratio that can be achieved with these devices and because neither large spaces nor special facilities are required to work with them. In addition, these devices are being installed in supercomputers, resulting in a significant boost of their computing capabilities and allowing hybrid architectures of CPUs and GPUs appear at the top of the ranking Top500 of supercomputers. Moreover, the emergence of providers of computing resources or Cloud Computing such as Amazon allows any user to access to high performance computing resources, on a pay as you go service (paying only for the use made). Furthermore, it is possible to configure and use a computer cluster with highperformance CPU nodes and integrated GPU cards just in few minutes if you have basic knowledge of the APIs of cloud providers. It is precisely in the research on the analysis of hyperspectral imagery for remote sensing of Earth that GPUs are allowing to reduce the processing time of this kind of images, which is essential owing to the scope of this field which includes actions such as fire detection, military target detection and the control of waste in the atmosphere or water among others. One of the best known algorithms for extracting pure spectral signatures or endmembers in multispectral or hyperspectral images is the algorithm Pixel Purity Index or PPI. This algorithm is highly relevant because hyperspectral images captured by current sensors used in remote sensing store a mixture of pure substances in each pixel of the spatial resolution. This means that a pixel represents an area, usually of a size around a square metre, which may show vegetation, water, buildings and so on. The PPI algorithm is included in the retail software package ENVI, nowadays belonging to the company Exelis Visual Information Solutions. The core of this work lies in the implementation of an efficient version of the PPI algorithm for a computer cluster whose nodes have GPU cards (or GPU cluster). For its development and test the GPU cluster of CETA-CIEMAT has been used, being its present location the town of Trujillo (Cáceres). The afore mentioned infrastructure consists of 17 compute nodes for the Test environment equipped with C1060 GPUs (S1070) from Nvidia and 32 compute nodes for the Production environment equipped with C2050 series GPUs (S2050) and C2070 (S2070) also from Nvidia. Our implementation of PPI is based on previously developed versions of this algorithm for computer clusters and GPU cards. For this reason, a comparative study of performance of these three parallel implementations is made and discussed. Moreover, in the present paper, the different versions of the algorithm for a GPU cluster are compared to show how performance has evolved from the first to the final version, the latter including several optimizations such as the use of multiple GPUs. Keywords Hyperspectral, Endmember, Pixel Purity Index, PPI, multiGPU, ENVI. xi xii Índice general 1. Motivaciones y objetivos 1.1. Motivaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3. Organización del documento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 2 2. Antecedentes 2.1. Análisis hiperespectral . . . . . . . . . . . . . . 2.1.1. Concepto de imagen hiperespectral . . . 2.1.2. Sensores hiperespectrales . . . . . . . . 2.2. Técnicas de análisis hiperespectral . . . . . . . 2.2.1. Pixel Purity Index (PPI) . . . . . . . . 2.2.2. Orthogonal Subspace Projection (OSP) 2.2.3. N-FINDR . . . . . . . . . . . . . . . . . 2.3. Necesidad de pararelismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 5 7 9 10 11 11 12 3. Arquitecturas paralelas consideradas 3.1. Procesamiento paralelo en clúster . . . . . 3.1.1. Message Passage Interface (MPI) . 3.2. Procesamiento paralelo en GPUs . . . . . 3.2.1. NvidiaTM CUDA . . . . . . . . . . 3.2.2. Escritura de programas en CUDA 3.3. El clúster de GPUs de CETA-CIEMAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 13 14 16 18 20 21 4. Método 4.1. Pixel Purity Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 27 5. Implementaciones paralelas 5.1. Implementación de PPI para un clúster de computadores . . . . 5.1.1. Estrategias de particionamiento de datos hiperespectrales 5.1.2. Detalle de la implementación . . . . . . . . . . . . . . . . 5.2. Implementación paralela de PPI para GPUs . . . . . . . . . . . . 5.3. Implementación paralela de PPI para un clúster de GPUs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 31 31 33 35 37 6. Resultados experimentales 6.1. Descripción de las pruebas realizadas . . . . . . . . . . . . 6.1.1. Parámetros del algoritmo PPI . . . . . . . . . . . . 6.1.2. Entornos de pruebas . . . . . . . . . . . . . . . . . 6.2. Desarrollo de PPI para el clúster de GPUs . . . . . . . . . 6.2.1. Implementación sin optimizaciones . . . . . . . . . 6.2.2. Implementación Zero Copy . . . . . . . . . . . . . 6.2.3. Implementación con uso de memoria local . . . . . 6.2.4. Implementación con optimizaciones de compilación 6.2.5. Implementación basada en memoria compartida . 6.2.6. Implementación multiGPU . . . . . . . . . . . . . 6.3. Comparativa frente a otras implementaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 39 39 43 43 43 50 53 58 61 66 70 . . . . . . 7. Conclusiones y trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 xiii xiv ÍNDICE GENERAL Lista de tablas 2.1. Caracterı́sticas de algunos sensores hiperespectrales ya en funcionamiento o que estarán disponibles a corto plazo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1. Caracterı́sticas de la imagen hiperespectral AVIRIS, capturada en 1995 sobre la región minera de Cuprite, En Nevada (Estados Unidos) . . . . . . . . . . . . . . . . . . . . . 6.2. Caracterı́sticas de la imagen hiperespectral AVIRIS, capturada en 2008 sobre la región del lago St. Clair, en Michigan (Estados Unidos) . . . . . . . . . . . . . . . . . . . . . 6.3. Caracterı́sticas de la imagen hiperespectral AVIRIS, capturada en 2009 sobre el Parque Nacional de Yellowstone, en Wyoming (Estados Unidos) . . . . . . . . . . . . . . . . . 6.4. Datos de ocupación de la GPU para la implementación MPI-CUDA sin optimizaciones del algoritmo PPI para el entorno de Test del clúster de CETA-CIEMAT . . . . . . . 6.5. Datos de ocupación de la GPU para la implementación MPI-CUDA sin optimizaciones del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT . . . 6.6. Resultados de ejecución (en sg.) de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . 6.7. Resultados de ejecución (en sg.) de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . 6.8. Resultados de ejecución (en sg.) de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . 6.9. Datos de ocupación de la GPU para la implementación MPI-CUDA Zero Copy del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT . . . . . . 6.10. Resultados de ejecución (en sg.) de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . 6.11. Resultados de ejecución (en sg.) de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . 6.12. Resultados de ejecución (en sg.) de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . 6.13. Datos de ocupación de la GPU para la implementación MPI-CUDA local memory del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT . . . . . . 6.14. Resultados de ejecución (en sg.) de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . 6.15. Resultados de ejecución (en sg.) de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . 6.16. Resultados de ejecución (en sg.) de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . 6.17. Datos de ocupación de la GPU para la implementación MPI-CUDA con optimizaciones de compilación del algoritmo PPI para el entorno de Producción del clúster de CETACIEMAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.18. Resultados de ejecución (en sg.) de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . 6.19. Resultados de ejecución (en sg.) de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . 6.20. Resultados de ejecución (en sg.) de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . 6.21. Datos de ocupación de la GPU para la implementación MPI-CUDA basada en memoria compartida utilizados para el cálculo del tamaño del array a almacenar en dicho nivel de la jerarquı́a de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv 9 40 40 41 44 45 47 48 49 51 51 52 53 54 55 56 57 58 59 60 60 62 xvi LISTA DE TABLAS 6.22. Datos de ocupación de la GPU para la implementación MPI-CUDA basada en memoria compartida del algoritmo PPI para el entorno de Producción del clúster de CETACIEMAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.23. Resultados de ejecución (en sg.) de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . 6.24. Resultados de ejecución (en sg.) de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . 6.25. Resultados de ejecución (en sg.) de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . 6.26. Datos de ocupación de la GPU para la implementación MPI-CUDA multiGPU del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT . . . . . . 6.27. Resultados de ejecución (en sg.) de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . 6.28. Resultados de ejecución (en sg.) de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . 6.29. Resultados de ejecución (en sg.) de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . 6.30. Comparativa de resultados obtenidos para las versiones secuencial, clúster de computadores, GPUs y clúster de GPUs en el procesamiento de la imagen Cuprite . . . . . . 6.31. Comparativa de resultados obtenidos para las versiones secuencial, clúster de computadores, GPUs y clúster de GPUs en el procesamiento de la imagen Lago St. Clair . . 6.32. Comparativa de resultados obtenidos para las versiones secuencial, clúster de computadores, GPUs y clúster de GPUs en el procesamiento de la imagen Yellowstone . . . 62 63 64 65 66 68 69 69 70 71 71 Lista de figuras 2.1. 2.2. 2.3. 2.4. Concepto de imagen hiperespectral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Resultado de la toma de datos por parte de un sensor hiperespectral . . . . . . . . . . Tipos de pı́xeles en imágenes hiperespectrales . . . . . . . . . . . . . . . . . . . . . . . Relación señal-ruido (SNR) en los diferentes canales de AVIRIS a lo largo de los últimos años (reproducido con permiso de Robert O. Green, investigador principal del proyecto AVIRIS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Representación de los modelos de mezcla en análisis hiperespectral: (a) Modelo de mezcla lineal (b) Modelo de mezcla no lineal . . . . . . . . . . . . . . . . . . . . . . . . Interpretación gráfica del modelo lineal de mezcla . . . . . . . . . . . . . . . . . . . . . Representación gráfica del funcionamiento del algoritmo Orthogonal Subspace Projection (OSP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Representación gráfica del funcionamiento del algoritmo N-FINDR . . . . . . . . . . . 11 12 3.1. Supercomputador Columbia, localizado en el Ames Research Center de NASA, California (Estados Unidos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2. Esquema hardware de una GPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3. Arquitectura GPU frente a CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4. Operaciones en coma flotante CPU frente a GPU . . . . . . . . . . . . . . . . . . . . . 3.5. Ancho de banda de memoria en GB/s de CPU frente a GPU . . . . . . . . . . . . . . 3.6. Esquema del modelo de programación CUDA . . . . . . . . . . . . . . . . . . . . . . . 3.7. Esquema del modelo de memoria CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . 3.8. Supercomputador Tianhe-1A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.9. Clúster de GPUs MinoTauro del BSC . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.10. Arquitectura del entorno HPC de CETA-CIEMAT . . . . . . . . . . . . . . . . . . . . 3.11. Disposición de los elementos hardware del clúster Tesla de GPUs de CETA-CIEMAT . 3.12. Disposición de los elementos hardware del clúster Fermi de GPUs de CETA-CIEMAT 14 17 17 18 18 19 20 22 22 23 24 25 4.1. Selección de los pı́xeles extremos para un conjunto de skewers en el algoritmo PPI . . 28 5.1. Estrategias de particionamiento de imágenes hiperespectrales. (a) Particionamiento espectral. (b) Particionamiento espacial. (c) Particionamiento hı́brido. . . . . . . . . . . 5.2. Particionamiento de datos en el algoritmo PPI . . . . . . . . . . . . . . . . . . . . . . 32 35 2.5. 2.6. 2.7. 2.8. 6.1. Imagen real AVIRIS perteneciente a la región minera de Cuprite, en Nevada (Estados Unidos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2. Imagen real AVIRIS perteneciente la región del lago St. Clair, en Michigan (Estados Unidos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3. Imagen real AVIRIS perteneciente al Parque Nacional de Yellowstone, en Wyoming (Estados Unidos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.4. Resultados visuales obtenidos de la ejecución del algoritmo PPI para las versiones de ENVI y clúster de GPUs sobre la imagen Cuprite . . . . . . . . . . . . . . . . . . . . . 6.5. Ejemplo de algunos de los minerales detectados en la imagen resultado de la ejecución de PPI sobre la imagen Cuprite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.6. Firmas espectrales de los minerales detectados en la imagen resultado de la ejecución de PPI para clúster de GPUs sobre la imagen Cuprite . . . . . . . . . . . . . . . . . . 6.7. Resultados visuales obtenidos de la ejecución del algoritmo PPI para las versiones de ENVI y clúster de GPUs sobre la imagen Lago St. Clair . . . . . . . . . . . . . . . . . xvii 5 6 7 8 9 10 40 41 42 45 46 46 47 xviii LISTA DE FIGURAS 6.8. Gráficas de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.9. Gráficas de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.10. Gráficas de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.11. Gráficas de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.12. Gráficas de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.13. Gráficas de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.14. Gráficas de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.15. Gráficas de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.16. Gráficas de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.17. Gráficas de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . 6.18. Gráficas de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . 6.19. Gráficas de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . 6.20. Gráficas de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.21. Gráficas de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . . . . 6.22. Gráficas de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . . . . 6.23. Gráficas de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.24. Gráficas de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.25. Gráficas de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 49 49 51 52 53 56 57 57 59 59 61 64 65 65 68 69 70 Capı́tulo 1 Motivaciones y objetivos 1.1. Motivaciones El presente trabajo se enmarca dentro de las lı́neas de investigación del grupo de Computación Hiperespectral (HYPERCOMP ), perteneciente al Departamento de Tecnologı́a de Computadores y Comunicaciones de la Universidad de Extremadura, y está dedicado a la implementación eficiente de un algoritmo de procesamiento de imágenes hiperespectrales de la superficie terrestre para la extracción de referencias espectrales puras o endmembers, haciendo uso de un clúster de computadores cuyos nodos de cómputo están equipados con tarjetas GPU. El algoritmo implementado es el llamado Pixel Purity Index o PPI. Las aplicaciones de este algoritmo son numerosas, entre ellas destacan aplicaciones militares (detección de targets u objetivos), detección y monitorización de fuegos y agentes contaminantes en atmósfera y agua (agentes quı́micos o vertidos), agricultura de precisión y detección de minerales, entre otras aplicaciones. El algoritmo PPI es un algoritmo costoso computacionalmente hablando, debido a las iteraciones que debe realizar sobre los pı́xeles de la imagen hiperespectral a procesar. Esta caracterı́stica provoca que los resultados de su ejecución se demoren demasiado en el tiempo, algo inadmisible dada la importancia de la respuesta temporal en las aplicaciones de dicho algoritmo. Además, la evolución de los sensores hiperespectrales provoca que el tamaño de las imágenes capturadas sea cada vez mayor, lo que también afecta al tiempo de ejecución del algoritmo. Este problema se puede generalizar a la mayorı́a de los algoritmos de análisis de imágenes multiespectrales o hiperespectrales. A dı́a de hoy, el problema de la mejora del rendimiento de los algoritmos de análisis hiperespectral se ha abordado, según la literatura, optando por la paralelización de los algoritmos adaptada a diferentes infraestructuras de cómputo, como clústers de computadores, sistemas multiprocesador, tarjetas gráficas programables de propósito general (GPUs) y sistemas reconfigurables. Todas las alternativas presentan sus ventajas e inconvenientes, por lo que, de momento, no existe una solución que se adapte mejor a todos los problemas. La computación clúster presenta buenos resultados de rendimiento y se adapta adecuadamente al problema, especialmente para el tratamiento de datos almacenados en un repositorio de datos en tierra. El inconveniente reside en el procesamiento de datos en tiempo real, debido a que los datos deben ser comprimidos y enviados desde el elemento de captura hasta la localización del clúster, lo que penaliza la respuesta temporal. Además por razones más que evidentes, dado el elevado peso, consumo energético y de espacio no es posible realizar una instalación de este tipo on-board. Por otro lado, las implementaciones de los algoritmos de análisis hiperespectral orientadas a tarjetas GPU están a niveles del estado del arte, debido a que son dispositivos de reciente aparición y a su excelente relación precio - rendimiento. Trabajos recientemente publicados exponen rendimiento de ejecución muy cercanos al tiempo real. Por contra, actualmente las tarjetas GPU tienen un alto coste energético, por lo que de momento no son viables para su uso a bordo, pero dada su rápida evolución y la inversión en eficiencia energética de los fabricantes, es cada dı́a más factible que lleguen a montarse junto a los dispositivos de captura. Por último, las sistemas reconfigurables son actualmente la implementación que menor coste energético lleva asociado y el menor peso asociado, lo que las hacen recomendables para su instalación a bordo. A dı́a de hoy, se han consiguido buenos resultados para algunos algoritmos de procesamiento de imágenes hiperespectrales, pero tienen el inconveniente asociado de la limitación de recursos en arquitecturas tolerantes a la radiación y al menor rendimiento de los algoritmos implementados frente 1 2 CAPÍTULO 1. MOTIVACIONES Y OBJETIVOS a las soluciones anteriores. Actualmente, el uso de tarjetas GPU aplicado a la computación cientı́fica es un hecho, debido a su buen rendimiento, al bajo coste de adquisición y la aparición de entornos de programación que facilitan la explotación de este tipo de recursos. Tanto es ası́ que los fabricantes están incluyendo este tipo de dispositivos en sus infraestructuras de clúster de computación, consiguiendo sistemas muy potentes que copan algunos de los puestos más altos del ranking de supercomputadores Top500. Este hecho, unido al reciente auge de la computación en la nube, mediante la cual un cienfı́fico o grupo de investigación puede tener acceso a recursos avanzados de supercomputación para su uso puntual a un precio razonable, hacen necesario la evaluación de rendimiento de este tipo de infraestructuras para su aplicación al análisis de imagen hiperespectral. Debido a esto, este trabajo tiene además como objetivo la elaboración de un estudio comparativo de rendimiento de varias implementaciones paralelas del algoritmo PPI, en sus versiones para clúster de computadores, GPU y clúster de GPUs. Esta última ha sido desarrollada especialmente para este trabajo y está orientada a su explotación en el clúster de GPUs de Centro Extremeño de Tecnologı́as Avanzadas (CETA), localizado en Trujillo (Cáceres). CETA es una subsede del Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas (CIEMAT), un organismo público de investigación perteneciente al Ministerio de Economı́a y Competitividad del Gobierno de España. 1.2. Objetivos Como se ha comentado en el apartado anterior este trabajo tiene como principal objetivo la implementación eficiente del algoritmo de análisis de imagen hiperespectral PPI orientado a una arquitectura de clúster de computadores cuyos nodos de cálculo montan tarjetas GPUs. Para alcanzar este objetivo se presentan a continuación un conjunto de objetivos especı́ficos a llevar a cabo para completar el objetivo final: 1. Realización de un estudio del algoritmo Pixel Purity Index en sus diferentes implementaciones, para identificar sus posibilidades de implementación para un clúster de GPUs. 2. Estudio del lenguaje de programación CUDA, de la librerı́a MPI y de sus posibilidades de uso conjunto para llevar a cabo la implementación del algoritmo. 3. Implementación del algoritmo PPI paralelo para su ejecución en el clúster de GPUs de CETACIEMAT y optimización del tiempo de ejecución. 4. Realización de un estudio comparativo de rendimiento de las implementaciones paralelas del algoritmo PPI en sus versiones para clúster de computadores, GPUs y clúster de GPUs. El siguiente apartado describe la organización de este documento para facilitar el seguimiento del trabajo realizado para alcanzar estos ebjetivos. 1.3. Organización del documento El presente documento se organiza de la siguiente forma: 1. Motivaciones y objetivos El presente capı́tulo describe las motivaciones y objetivos del trabajo realizado. También se describe la organización general del documento. 2. Antecedentes En este capı́tulo se describen los conceptos fundamentales relacionados con el análisis de imágenes hiperespectrales, desde el concepto de imagen hiperespectral hasta los conceptos básicos relativos a la extracción de firmas espectrales puras. Además, este capı́tulo introduce los antecedentes relacionados con la aplicación de técnicas de computación paralela a problemas de análisis hiperespectral, describiendo la necesidad de pararalelismo y capacidad de procesamiento en tiempo real para determinados contextos de aplicación. 3. Arquitecturas paralelas consideradas En este apartado se describen las caracterı́sticas de la arquitectura paralela considerada, el clúster de GPUs de CETA-CIEMAT, haciendo un repaso de los clúster de computadores y de las tarjetas gráficas programables (GPUs), que son la base de esta solución hı́brida de cómputo, enumerando sus ventajas e incovenientes. Se presentan también las herramientas de programación utilizadas para el desarrollo de la implementación 1.3. ORGANIZACIÓN DEL DOCUMENTO 3 de PPI para el clúster de GPUs: la librerı́a Message Passing Interface (MPI) y el entorno de desarrollo para Nvidia CUDA. 4. Método En este capı́tulo se describe y muestra la utilidad del algoritmo PPI para el análisis de imágenes hiperespectrales, ası́ como su funcionamiento y filosofı́a. 5. Implementaciones paralelas Es esta sección se presentan las implementaciones paralelas del algoritmo PPI para clúster de computadores, tarjetas GPUs y clúster de GPUs (implementada en el marco de este trabajo) utilizadas para la realización del estudio comparativo de rendimiento que se describe en el capı́tulo posterior. 6. Resultados experimentales Este capı́tulo se divide en dos partes: la primera realiza un análisis comparativo de las diferentes evoluciones del algoritmo paralelo PPI para clúster de GPUs implementadas durante el desarrollo de este trabajo, y la segunda compara los resultados obtenidos de la versión de mejor rendimiento en el clúster de GPUs con las versiones de PPI para clúster de computadores y GPUs descritas en el capı́tulo anterior. 7. Conclusiones y trabajo futuro Este último capı́tulo está dedicado a la exposición de las conclusiones obtenidas tras la realización de este trabajo y a la exposición de una propuesta de trabajo futuro. 4 CAPÍTULO 1. MOTIVACIONES Y OBJETIVOS Capı́tulo 2 Antecedentes En este capı́tulo se describen los conceptos fundamentales relacionados con el análisis de imágenes hiperespectrales. En primer lugar se describe el concepto de imagen hiperespectral, detallando sus particularidades y caracterı́sticas. A continuación, se describen los sensores de adquisición de este tipo de imágenes, destacando el sensor utilizado para capturar las imágenes incluidas en este trabajo. Posteriormente, se da una visión general de las técnicas de extracción de firmas espectrales puras para finalizar exponiendo la necesidad de aplicar técnicas de computación paralela al análisis hiperespectral. 2.1. Análisis hiperespectral En este apartado se definen algunos conceptos básicos asociados al análisis de imágenes hiperespectrales de la superficie terrestre. Primeramente, se define el concepto de imagen hiperespectral, detallando particularidades y caracterı́sticas propias de este tipo de imágenes de alta dimensionalidad para pasar a continuación a la descripción de los sensores de adquisición de este tipo de datos, haciendo hincapié en los utilizados en la captura de las imágenes usadas en este trabajo. 2.1.1. Concepto de imagen hiperespectral El análisis hiperespectral es una técnica avanzada de observación remota de la tierra caracterizada por la disponibilidad de imágenes con gran resolución en el dominio espectral (cientos o miles de bandas espectrales [14]). La evolución de las técnicas de análisis de datos hiperespectrales está marcada principalmente por la evolución de los dispositivos de captura y la instrumentación avanzada para la observación remota de la Tierra [38]. Los primeros sensores eran fundamentalmente de carácter espacial. Poco después aparecieron instrumentos capaces de medir singularidades en el espectro de la luz reflejada por los diferentes materiales presentes en el mundo real, utilizando varios canales espectrales, provocando la necesidad del desarrollo de técnicas de análisis hiperespectral [19]. Figura 2.1: Concepto de imagen hiperespectral Los sensores hiperespectrales actuales permiten la adquisición de imágenes digitales con un gran número de canales espectrales muy cercanos entre sı́, obteniendo para cada porción de la escena o pı́xel una firma espectral caracterı́stica para cada material [16]. Si se representa una imagen hiperespectral de forma gráfica, se obtendrı́a un cubo cuyas dos primeras dimensiones, X e Y , corresponderı́an con 5 6 CAPÍTULO 2. ANTECEDENTES las coordenadas espaciales de un pı́xel determinado en la imagen, y la dimensión Z representarı́a la singularidad espectral de cada pı́xel según las diferentes longitudes de onda [9]. La figura 2.1 muestra una representación gráfica del concepto de imagen hiperespectral. Como se puede observar en la figura 2.1, las dimensiones espaciales de la imagen presentan los conceptos de lı́nea y muestra, donde las lı́neas horizontales corresponderı́an a las lı́neas de la imagen hiperespectral y las verticales a las muestras. Además, se presenta el concepto de banda o canal, que representa la tercera dimensión de la imagen hiperespectral. Cada banda almacenará el valor capturado para cada longitud de onda de trabajo del sensor hiperespectral. Por lo tanto, cada pı́xel situado en la coordenada (x, y) es un vector de valores discretos, en el que cada elemento del vector corresponde a una de las longitudes de onda empleadas en el proceso de adquisición de la imagen. El resultado de la toma de datos sobre una determinada escena puede ser por lo tanto representado en forma de cubo, con dos dimensiones para representar la ubicación espacial de un pı́xel y una tercera que representa la singularidad espectral de cada pı́xel en diferentes longitudes de onda. Si denotamos una imagen hiperespectral como un cubo de datos F , se puede representar un pı́xel del imagen de coordenadas de resolución espacial (x, y) como F (x, y). La figura 2.2 ilustra el proceso de captura de una imagen hiperespectral de forma simplificada, tomando como dispositivo de ejemplo el sensor AVIRIS de NASA, que trabaja en un rango de logitudes de onda entre 400 y 2.500 nm. Haciendo analogı́a con el procedimiento de toma de una foto con una cámara digital convencional, los sensores hiperespectrales son capaces de hacer “fotos” mucho más avanzadas, cuyos pı́xeles no solo almacenan los valores RGB, sino que además incluyen el resto de los valores espectrales en los que también trabaja el sensor (224 en total en el caso de AVIRIS). Con estos valores se obtiene una firma espectral caracterı́stica que será utilizada en el proceso de análisis de la imagen [10]. Figura 2.2: Resultado de la toma de datos por parte de un sensor hiperespectral Como consecuencia del proceso de captura de imágenes por los sensores hiperespectrales, hay que destacar que en este tipo de imágenes es frecuente encontrar mezclas a nivel de subpı́xel. Este hecho se debe principalmente a la insuficiente resolución espacial del sensor, normalmente del orden de varios metros cuadrados por pı́xel, para separar materiales espectralmente puros. Este fénomeno no es exclusivo de los sensores hiperespectrales, dado que ocurren de forma frencuente en la naturaleza y a todas las escalas [38, 21, 23, 29]. Consecuentemente podemos clasificar los pı́xeles de una imagen de forma general en dos grupos: Pı́xeles puros: aquellos en los que solo aparece un tipo de material (por ejemplo agua). Pı́xeles mezcla: aquellos en los que coexisten diferentes materiales a nivel de subpı́xel (por ejemplo vegetación y suelo). La mayor parte de los pı́xeles de una imagen hiperespectral son pı́xeles mezcla frente a los pı́xeles puros, que pueden no existir en la propia imagen. La figura 2.3 muestra un ejemplo de adquisición de pı́xeles puros (a nivel macroscópico, es decir, si el tamaño del pı́xel de la imagen no es lo suficientemente grande para separar los diferentes materiales) y mezcla en imágenes hiperespectrales. 2.1. ANÁLISIS HIPERESPECTRAL 7 Figura 2.3: Tipos de pı́xeles en imágenes hiperespectrales Otros factores que influyen en la obtención de pı́xeles mezcla son los efectos atmosféricos, los efectos de dispersión múltiple debido a materiales que reflejan la luz de forma irregular (por ejemplo, algunos tipos de vegetación o algunos minerales, entre otros), por lo que dicho fenómeno, y su caracterización, es de gran interés en el desarrollo de técnicas de análisis de imágenes hiperespectrales. Para finalizar, comentar que un pı́xel mezcla puede descomponerse en una colección de espectros de pı́xeles puros, denominados endmembers en la terminologı́a y en un conjunto de valores o abundancias que indican la proporción o contribución individual de cada uno de los endmembers al valor espectral del pı́xel mezcla. En los apartados siguientes se describen los modelos utilizados para describir esta situación denominada modelo de mezcla. Además se describen los sensores espectrales, haciendo hincapié en el sensor AVIRIS, utilizado para la adquisición de las imágenes consideradas en este trabajo. 2.1.2. Sensores hiperespectrales Hoy en dı́a, existe un amplio abanico de sensores hiperespectrales para la observación remota de la Tierra. Según [26], se puede definir un sensor hiperespectral como un dispositivo capaz de medir la radiación reflejada en una gran cantidad de bandas muy estrechas. Este tipo de sensores son capaces de distinguir materiales, sustancias y objetos que los sensores multiespectrales no son capaces de distinguir. Para cada pı́xel capturado se obtiene una firma espectral, que agrupa los valores de reflectancia obtenidos para cada longitud de onda de trabajo del sensor, lo que permite una caracterización muy precisa de la superficie terrestre [19, 20]. 8 CAPÍTULO 2. ANTECEDENTES Los sensores hiperespectrales pueden clasificarse según el modo en el que son transportados durante el procedimiento de captura de datos [19, 20]. Actualmente, la mayor parte son aerotransportados, como el Airborne Visible/Infrared Imaging Spectrometer (AVIRIS) de la NASA, utilizado para obtener las imágenes que apoyan este trabajo. Un ejemplo de sensor de satélite es el Hyperion, también de NASA y también son comunes los sensores de mano para muchos estudios de desarrollo y validación. AVIRIS es un sensor hiperespectral aerotransportado, con capacidades analı́ticas en las zonas visible e infrarroja del espectro [16]. Comenzó a funcionar en 1987, siendo el primer sistema de adquisición de imágenes capaz de obtener información en una gran cantidad de bandas espectrales estrechas y contiguas. AVIRIS puede obtener información espectral de 224 canales espectrales, que abarcan un rango de longitudes de onda entre 400 y 2.500 nanómetros, con un ancho entre bandas muy pequeño, de aproximadamente 10 nanómetros. En 1989, AVIRIS pasó a ser un instrumento aerotransportado con el que se han obtenido imágenes de Estados Unidos, Canadá y Europa. Hasta el dı́a de hoy, se han utilizado cuatro plataformas diferentes en las campañas de toma de datos [15]. En cuanto a las caracterı́sticas propias del sensor hiperespectral de AVIRIS, se pueden destacar las siguientes: El sensor utiliza un explorador de barrido que permite obtener un total de 614 pı́xeles por cada oscilación. Posee un conjunto de espectrómetros que se pueden clasificar en dos grupos: • Para la parte visible del espectro se utiliza el espectrómetro EFOS-A, que está compuesto por un array de 32 detectores lineales. • Para la cobertura del infrarrojo se utilizan los espectrómetros EFOS-B, EFOS-C y EFOS-D, que están compuestos por arrays de 64 detectores lineales. Dispone de un sistema de calibración a bordo, el cual hace uso de una lámpara halógena de cuarzo que proporciona la radiación necesaria para comprobar el estado de los diferentes espectrómetros. La señal medida por cada detector se amplifica y codifica utilizando 12 bits. Esta señal se almacena en una memoria intermedia donde es sometida a una etapa de preprocesado, siendo registrada a continuación en una cinta de alta densidad de 10,4 GB a una velocidad de 20,4 MB/s. A lo largo de los años el sensor ha ido mejorando sus prestaciones en cuanto a la relación señalruido o signal to noise ratio (SNR). La figura 2.4 muestra una descripción de las prestaciones del sensor en cuanto a la relación SNR (para cada una de sus 224 bandas) a lo largo de los años en los que el sensor ha estado en funcionamiento [38]. Figura 2.4: Relación señal-ruido (SNR) en los diferentes canales de AVIRIS a lo largo de los últimos años (reproducido con permiso de Robert O. Green, investigador principal del proyecto AVIRIS) 2.2. TÉCNICAS DE ANÁLISIS HIPERESPECTRAL Paı́s Resolución espacial Resolución temporal Rango espectral Resolución espectral Cobertura imágenes Cobertura terrestre Fecha lanzamiento Tiempo de vida previsto 9 Hyperion1 EnMAP2 HyspIRI3 Estados Unidos 30 m 16 dı́as 400-2500 nm 10 nm 7,7 km Parcial 2000 10 años Alemania 30 m 4 dı́as 420-2450 nm 0,5-10 nm 30 km Total 2012 6 años Estados Unidos 60 m 18 dı́as 380-2500 nm 10 nm 120 km Total 2018 6 años Tabla 2.1: Caracterı́sticas de algunos sensores hiperespectrales ya en funcionamiento o que estarán disponibles a corto plazo A pesar de que AVIRIS puede considerarse como un sensor hiperespectral de referencia, existen otros sensores hiperespectrales de gran relevancia para la comunidad cientı́fica. La tabla 2.1 presenta algunas de las caracterı́sticas de otros sensores ya en funcionamiento o de puesta en funcionamiento en breve. 2.2. Técnicas de análisis hiperespectral Como se comentaba en el apartado 2.1, la mayorı́a de las técnicas desarrolladas de análisis hiperespectral presuponen una mezcla a nivel de subpı́xel y se debe principalmente a la insuficiente resolución espacial del sensor. Pero este fenómeno no es exclusivo del análisis hiperespectral, dado que ocurre de forma natural en el mundo real, a todas las escalas, por lo que el diseño de técnicas que sean capaces de modelarlo de forma adecuada resulta imprescindible. Dentro de las técnicas de análisis hiperespectral podemos distinguir dos grupos, según el modelo de mezcla que suponen: Modelo lineal: Asume que los elementos observados se relacionan de forma lineal durante el proceso de observación (ver figura 2.5 (a)). Modelo no lineal: Asume que los componentes se distribuyen de forma aleatoria, pudiendo producirse incluso defectos de dispersión múltiple durante el proceso de observación (ver figura 2.5 (b)). Figura 2.5: Representación de los modelos de mezcla en análisis hiperespectral: (a) Modelo de mezcla lineal (b) Modelo de mezcla no lineal En análisis hiperespectral, el modelo lineal de mezcla es el más utilizado, debido a su sencillez y generalidad. Su objetivo es encontrar los vectores más extremos, conocidos como componentes espectralmente puros, pı́xeles puros o endmembers, y utilizarlos para “desmezclar” el resto de pı́xeles de la imagen mediante algún modelo de mezcla (linear mixture model ). En este modelo, cada pı́xel puede 1 http://eo1.gsfc.nasa.gov 2 http://www.enmap.org 3 http://hyspiri.jpl.nasa.gov 10 CAPÍTULO 2. ANTECEDENTES expresarse como una combinación de las firmas espectrales asociadas a los endmembers. Ası́ pues, es crucial saber identificar los endmembers adecuadamente. El modelo lineal de mezcla puede interpretarse de forma gráfica en un espacio bidimensional utilizando un diagrama de dispersión entre dos bandas poco correlacionadas de la imagen, tal y como se muestra en la figura 2.6). En ella puede apreciarse que todos los puntos de la imagen quedan englobados dentro del triángulo formado por los tres puntos más extremos (elementos espectralmente más puros). Los vectores asociados a estos puntos constituyen un nuevo sistema de coordenadas con origen en el centroide de la nube de puntos, de forma que cualquier punto de la imagen puede expresarse como combinación lineal de los puntos más extremos, siendo éstos seleccionados como mejores candidatos a ser endmember [6]. Figura 2.6: Interpretación gráfica del modelo lineal de mezcla Los algoritmos de extracción de firmas espectrales puras se pueden clasificar atendiendo a varios criterios como el enfoque (estadı́sticos y geométricos) y la hipótesis de partida (presencia o ausencia de endmembers), entre otros. Destacamos la clasificación según la información considerada, que agrupa a los algoritmos de extracción de endmembers en dos grupos: Algoritmos basados en la información espectral: Realizan la búsqueda de los pı́xeles más puros considerando solo la información espectral. Algoritmos basados en la información espectral y espacial: durante la búsqueda de los endmembers combinan el uso de la información espacial y espectral (generalmente mediante morfologı́a matemática o crecimiento de regiones). El algoritmo paralelizado en el marco de este trabajo es el Pixel Purity Index o PPI. Este algoritmo se incluye dentro de los algoritmos basados en la información espectral. Otros algoritmos que pertenecen a este grupo atendiendo a la misma clasificación son el Orthogonal Subspace Projection (OSP) y el N-FINDR. Para conocer el funcionamiento de estos algoritmos se describen brevemente a continuación. 2.2.1. Pixel Purity Index (PPI) El algoritmo Pixel Purity Index o PPI [7] fue desarrollado por Boardman, Kruse y Green en 1993 y se basa en la generación repetitiva de vectores unitarios o skewers con orientación aleatoria en la nube de puntos que forman la imagen hiperespectral. Cada punto de la imagen hiperespectral se proyecta sobre cada skewer identificando los puntos extremos en la dirección definida por éste e incrementando un contador asociado a estos puntos. Tras la ejecución de un número de iteraciones determinado se obtiene una imagen de pureza formada por los valores de los contadores asociados a cada pı́xel de resolución espacial de la imagen. El conjunto final de endmembers se extrae a través de un proceso interactivo de análisis y visualización. Este algoritmo ha sido objeto de paralelización en el marco de este trabajo y se definirá con más detalle en el capı́tulo 4. 2.2. TÉCNICAS DE ANÁLISIS HIPERESPECTRAL 2.2.2. 11 Orthogonal Subspace Projection (OSP) El algoritmo Orthogonal Subspace Projection o OSP fue desarrollado por Harsanyi y Chang en 1994 [17] para encontrar firmas espectrales utilizando el concepto de proyecciones ortogonales [34]. El algoritmo hace uso del concepto de proyección ortogonal, que viene dado por la fórmula 2.1, donde U es una matriz de firmas espectrales, UT es la traspuesta de dicha matriz e I es la matriz identidad. PU⊥ = I − U (UT U )−1UT (2.1) El proceso efectuado por el algoritmo puede resumirse en los siguientes pasos: 1. Seleccionar el vector más brillante de la imagen como pı́xel inicial (primer endmember ). 2. Aplicar un operador de proyección ortogonal al resto de los pı́xeles de la imagen para identificar aquel que sea más ortogonal con respecto al primer pı́xel seleccionado (segundo endmember ). 3. Aplicar de nuevo un operador de proyección ortogonal al resto de pı́xeles para identificar aquel que sea más ortogonal a los dos endmembers seleccionados previamente. 4. Repetir el proceso hasta que seleccionemos el número de endmembers deseado. Figura 2.7: Representación gráfica del funcionamiento del algoritmo Orthogonal Subspace Projection (OSP) 2.2.3. N-FINDR El algoritmo N-FINDR fue desarrollado por Winter en 2003 y utiliza una técnica basada en identificar los endmembers como los vértices del simplex de mayor volumen que puede formarse en el conjunto de puntos. N-FINDR no trabaja con todo el cubo de datos sino con una simplificación del mismo a tantas bandas como endmembers se deseen encontrar. Para este tipo de reducciones se suele utilizar la técnica Principal Component Analysis o PCA [18] o Minimum Noise Fraction o MNF [41]. El único parámetro que tiene este algoritmo es el número de endmembers a identificar. El funcionamiento del algoritmo se describe en los siguientes pasos: 1. Aplicar una reducción dimensional empleando el algoritmo PCA, que ordena las bandas de la imagen en términos de la varianza, o el MNF, que ordena las bandas de la imagen en términos de el ratio señal/ruido. 2. Realizar una selección inicial de N endmembers de forma aleatoria. 3. Probar cada pı́xel de la imagen en la posición de cada posible endmember manteniendo aquellos reemplazamientos que resulten en un incremento del volumen contenido en el hiperpolı́gono formado por los endmembers. 4. Repetir el proceso hasta que no se produzcan reemplazamientos. 12 CAPÍTULO 2. ANTECEDENTES Figura 2.8: Representación gráfica del funcionamiento del algoritmo N-FINDR 2.3. Necesidad de pararelismo El proceso de captura de datos por los sensores hiperespectrales de observación remota de la Tierra produce enormes cantidades de datos de gran dimensionalidad que deben ser almacenados y tratados de forma eficiente. Instituciones como NASA o la Agencia Espacial Europea (ESA) producen del orden de Terabytes de datos hiperespectrales diariamente y la falta de infraestructuras dedicadas al procesamiento de estos datos está dando lugar a que una parte significativa de los mismos acabe siendo almacenada en una base de datos para su procesamiento en el futuro. Esto en ocasiones no se materializa debido al alto coste computacional que supone el almacenamiento, procesamiento y distribución de este tipo de imágenes de forma eficiente. Los algoritmos de análisis hiperespectral anteriormente descritos se basan en operaciones matriciales muy costosas desde el punto de vista computacional [32]. Sin embargo, el carácter repetitivo de estas operaciones las hace susceptibles a ser paralelizadas, con el fin de aumentar el rendimiento de los algoritmos y disminuir el tiempo de generación del resultado, algo esencial teniendo en cuenta los campos de aplicación del análisis hiperespectral como la detección de incendios, el control de vertidos a la atmósfera y el agua y la detección de objetivos militares, entre otros. Las técnicas de computación paralela han sido ampliamente aplicadas al procesamiento de imágenes de gran dimensionalidad, facilitando la obtención de tiempos muy reducidos y pudiendo utilizar diferentes tipos de arquitecturas [33, 39, 27]. Acualmente, debido a la facilidad de acceso a arquitecturas paralelas de bajo coste, como GPUs, y a los servicios de computación de altas prestaciones ofrecidos por los proveedores de Cloud Computing como Amazon, existe actualmente una comunidad de investigadores muy activa dedicada al desarrollo de algoritmos eficientes de procesamiento de imágenes hiperespectrales. Este trabajo tiene como principal objetivo la implementación de una versión del algoritmo PPI para clúster de GPUs, una arquitectura paralela resultado de la incorporación de la tarjetas GPUs a un clúster de computadores de memoria distribuida. El siguiente capı́tulo describe el uso de clúster de computadores y de GPUs para el procesamiento de imágenes hiperespectrales y describe en detalle la infraestructura utilizada para el desarrollo y la ejecución de las pruebas realizadas en el marco de este trabajo: el clúster de GPUs de CETA-CIEMAT. Capı́tulo 3 Arquitecturas paralelas consideradas En este apartado se describen las caracterı́sticas de la arquitectura paralela objetivo: el clúster de GPUs de CETA-CIEMAT, haciendo un repaso de los clúster de computadores y de las tarjetas gráficas programables (GPUs), que son la base de esta solución hı́brida de cómputo, enumerando sus ventajas e incovenientes. Se presentan también las herramientas de programación utilizadas para el desarrollo de la implementación de PPI para el clúster de GPUs: la librerı́a Message Passing Interface (MPI) y el entorno de desarrollo para GPUs Compute Unified Device Architecture (CUDA) de Nvidia. 3.1. Procesamiento paralelo en clúster Hoy en dı́a todavı́a no se conoce el origen exacto del término computación clúster. Algunos estudios atribuyen dicho origen a las investigaciones realizadas por Amdhal [5], quien acuñó las bases del procesamiento paralelo con la denominada Ley de Amdhal, la cual permite determinar la mejora máxima que puede alcanzar un sistema cuando sólo una parte del mismo se mejora. Esta ley permite describir de forma matemática el factor de aceleración o speedup que se puede conseguir paralelizando un bloque de código en una determinada arquitectura. La fórmula 3.1 define el cálculo del speedup conseguido de una versión paralela de un cógido frente a su versión secuencial. T1 es el tiempo de ejecución de código secuencial y TP el tiempo de la versión paralela. S = T1 /TP (3.1) En la actualidad el término clúster de computadores o clúster se refiere habitualmente a una red de computadores (interconectados entre sı́ mediante una red de comunicación) que trabajan de forma conjunta como un único recurso computacional, abstrayendo al usuario de la infraestructura fı́sica de interconexión y siendo a ojos de éste una sola máquina con una capacidad de cómputo mayor a la habitual. Un ejemplo de clúster utilizado para el procesamiento de datos hiperespectrales es el supercomputador Columbia, localizado en el centro Ames Research Center (ARC) de NASA, en California (Estados Unidos). Consta de 10.240 procesadores y conexión de red de baja latencia de tipo Infiniband. Otro ejemplo de clúster de computadores utilizado esporádicamente para el tratamiento de datos hiperespectrales es el Mare Nostrum, localizado en el Barcelona Supercomputing Center (BSC) en Barcelona (España). El Mare Nostrum también consta de 10.240 núcleos de computación y conexión a red de baja latencia de tipo Myrinet (ver figura 3.1). Los clúster de computación han supuesto desde su aparición una revolución en el cálculo cientı́fico, principalmente por sus capacidades de procesamiento de grandes volúmenes de datos. Pueden estar compuestos por máquinas con la misma configuración hardware y de sistema operativo (clúster homogéneo), de diferente configuración hardware y sistemas operativos similares (clúster semi-homogéneo) o bien disponer de hardware de diferente rendimiento con distintos sistemas operativos (clúster heterogéneo). A continuación se enumeran algunas de las caracterı́sticas genéricas ofrecidas por este tipo de infraestructuras: Alto rendimiento: Los clústers ofrecen un rendimiento superior, lo que los hace indicados para la resolución de problemas de mayor tamaño. La capacidad de cálculo de un clúster es 13 14 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS Figura 3.1: Supercomputador Columbia, localizado en el Ames Research Center de NASA, California (Estados Unidos) generalmente superior a la de un ordenador más caro que el conjunto de máquinas que componen el clúster [8]. Además, es relativamente sencillo incorporar nuevas máquinas a la red de computadores para aumentar la capacidad de cómputo y realizarlo de manera transparente al usuario. Alta disponibilidad: En un clúster, el fallo de uno de los equipos conectados a la red no supone la pérdida total del servicio. Al contar con múltiples nodos, en caso de error es relativamente fácil extraer el nodo con problemas de la red y reemplazarlo por otro, sin que afecte al funcionamiento del clúster. Coste: Generalmente, un clúster se compone de múltiples máquinas de medio-bajo coste, fabricados a gran escala y que utilizan sistemas operativos estándar. En general, la construcción de un clúster es sencilla y relativamente económica, especialmente en los clúster homogéneos. En el caso de los clústers heterogéneos, es posible incluso implementar algoritmos capaces de administrar la carga de cada nodo de acuerdo a las caracterı́sticas del mismo [30, 31]. Por otro lado, los costes de mantenimiento de un clúster son altos, debido a la demanda energética de los nodos de cálculo, del propio hardware de interconexión y de los sistemas de refrigeración necesarios para su adecuado funcionamiento. Además, al estar basados en máquinas de bajo coste, por lo general es necesario disponer de un gran espacio fı́sico para su instalación. La popularidad de uso que alcanzaron los clústers provocó la necesidad de definir una serie de librerı́as estándar que permitiesen a los usuarios y desarrolladores la utilización y control de este tipo de sistemas de una manera más o menos intuitiva y estándar, que permitiesen la mayor abstracción posible respecto al hardware de ejecución. Ejemplos de este tipo de librerı́as son Parallel Virtual Machine (PVM) [40] y Message Passage Interface (MPI) [24]. Ésta última es la librerı́a utilizada mayoritariamente por los clústers actuales, por lo que se describe brevemente a continuación. 3.1.1. Message Passage Interface (MPI) Según [3], MPI es un estándar que define la sintaxis y la semántica de las funciones contenidas en una biblioteca de paso de mensajes diseñada para ser utilizada en programas que exploten la existencia de múltiples procesadores. Actualmente es el estándar de comunicación entre los nodos que ejecutan un determinado código en un sistema de memoria distribuida, como los clúster de computadores. MPI no consiste en una implementación de una librerı́a, sino que se considera un protocolo de comunicación entre máquinas. Debido a esto, existen multitud de implementaciones o “sabores” desarrollados en diferentes lenguajes como C, C++, Fortran y ADA, entre otros. La principal ventaja de MPI sobre otras bibliotecas de paso de mensajes es que se asegura la portabilidad de los programas que hacen uso de la librerı́a, siempre que no se haga uso de funciones especı́ficas de una implementación. Además, las implementaciones de MPI están optimizadas para una infraestructura hardware especı́fica (por 3.1. PROCESAMIENTO PARALELO EN CLÚSTER 15 ejemplo, una red de comunicaciones Infiniband), aumentando por consiguiente el rendimiento de las operaciones de comunicación para dicha infraestructura. Los mensajes en MPI están compuestos por el cuerpo, que contiene los datos que van a ser enviados, y su envoltura, que contiene el emisor y el receptor del mensaje. El cuerpo de un mensaje MPI está formado por tres partes: Buffer: Dirección del área de memoria donde se encuentran los datos a enviar o donde se almacenarán los datos recibidos. Tipo de dato: Corresponde al tipo de los datos que se envı́a. En los casos simples es un tipo primitivo, análogo a los tipos de cualquier lenguaje de programación como C. En los casos de aplicaciones avanzadas, puede ser incluso una estructura formada por varios tipos de datos. Contador: Es un número de secuencia, que junto con el tipo de datos, permite agrupar datos de un mismo tipo en un solo mensaje. MPI estandariza los tipos de datos primitivos, evitando que el desarrollador del código se preocupe por las diferencias que existan entre ellos cuando interaccionan varias plataformas. Los mensajes MPI contienen la fuente del mensaje, su destinatario, e información adicional que se necesite para la transmisión y entrega del mensaje. Esta información consta de un identificador o comunicador al que pertenecen los procesos fuente y destino y una etiqueta también llamada tag que sirve para identificar el mensaje entre el conjunto de mensajes que pueda recibir el proceso. El número de procesos requeridos para la ejecución del programa se asigna previamente, y no se crean procesos adicionales mientras dure la ejecución del mismo. A cada proceso se le asigna un identificador, accesible a través de la variable rank, en el rango {0 . . . P − 1} donde P es el número de procesos. A través del uso de rank se puede determinar qué proceso ejecuta una determinada porción de código. Esta variable contiene el identificador asignado a un proceso dentro del comunicador al que pertenece. Por otro lado, el comunicador engloba la colección de procesos que pueden enviarse mensajes entre sı́. Existe un comunicador básico llamado MPI COMM WORLD (nombre de la macro en C) que agrupa todos los procesos activos durante la ejecución de una determinada aplicación. Las funciones que define MPI se pueden clasificar en tres grupos diferenciados: Funciones de inicialización, administración y finalización de las comunicaciones: Estas funciones permiten la inicialización y finalización de la biblioteca de mensajes, la obtención del número de procesos y el identificador del proceso (rank ) dentro del comunicador. Son cuatro funciones que se utilizan en todo programa MPI, a saber: • MPI Init: Inicializa la sesión MPI. Esta función debe utilizarse antes de cualquier otra llamada a una función MPI. • MPI Comm size: Obtiene el número total de procesos que pertenece a un comunicador. • MPI Comm rank: Obtiene el identificador del proceso o rank dentro del comunicador al que pertenece. • MPI Finalize: Finaliza la sesión MPI. Esta función debe ser la última de la librerı́a utilizada dentro de un programa MPI. Permite liberar la memoria utilizada por la librerı́a. Funciones de transferencia de datos punto a punto • MPI Send: Permite el envı́o de información desde un proceso a otro. Esta función es bloqueante, es decir, que el proceso que realiza la llamada se bloquea hasta que la operación de comunicación se complete. Este tipo de llamadas devuelve un código que indica el éxito o el fracaso de la operación. • MPI Recv: Permite la recepción de información desde otro proceso. Es una operación bloqueante. • MPI Isend: Versión de MPI Send no bloqueante. Las llamadas no bloqueantes deben finalizar explı́citamente con llamadas como MPI Wait. • MPI IRecv: Versión de MPI Recv no bloqueante. • MPI Wait: Llamada bloqueante que finaliza cuando una operación de envı́o o recepción se completa. 16 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS • MPI Test: Permite comprobar si una operación de envı́o o recepción ha finalizado. Tras comprobar el estado retorna. Funciones de transferencia a varios procesos: Estas funciones son conocidas como operaciones colectivas o grupales, ya que están orientadas a un grupo de procesos. Este tipo de funciones incluyen operaciones de difusión (broadcasting), reparto (scattering), recolección (gathering) y reducción (reduction). Las funciones más populares de este grupo son las siguientes: • MPI Barrier: Permite realizar una operación de sincronización de procesos. Esta sincronización no conlleva ningún intercambio de información y suele emplearse para dar por finalizada una etapa del programa esperando a que todos los procesos finalicen sus tareas antes de comenzar la siguiente. • MPI Bcast: Permite a un proceso enviar una copia de sus datos al resto de procesos dentro del grupo definido por su comunicador. En el caso del comunicador global (MPI COMM WORLD), se enviarı́a a todos los demás procesos. • MPI Scatter: Es similar a la operación MPI Bcast, pero en este caso el dato a enviar se reparte de forma equitativa entre el resto de procesos. • MPI Gather: Es la operación contraria a MPI Scatter. Permite a un proceso realizar una recolección de datos de un conjunto de procesos, agrupándolos en un área de memoria común. • MPI Reduce: Permite la recolección de datos de un conjunto de procesos, con la singularidad de que se realiza una combinación de éstos (por ejemplo, una suma o un promedio) y se genere un resultado. La especificación actual de MPI es la 2.2 y fue aprobada por el MPI Forum en Septiembre del 2009. Aunque es la última especificación, todas las funciones descritas en este apartado pertenecen a la primera especificación, cuya última versión (1.3) fue aprobada en Julio de 2008. La segunda especificación consta mayoritariamente de funciones avanzadas de comunicación, por lo que actualmente existe un gran grupo de usuarios que todavı́a utilizan la primera versión al cubrir ésta sus necesidades. Como dato adicional, la tercera versión de la especificación está en proceso de borrador y se estima que se aprobará en aproximadamente un año (2013). Esta nueva especificación incluirá, por ejemplo, las versiones no bloqueantes de las operaciones colectivas. El MPI Forum ha publicado una web1 donde se puede encontrar información del estado del borrador y de las discusiones sobre MPI 3.0. Para finalizar, hay que comentar que la mayorı́a de las funciones descritas en este apartado se han utilizado en las implementaciones del algoritmo PPI para clúster y clúster de GPUs implementadas en el marco de este trabajo. Se describirá su uso en detalle en el capı́tulo 5. 3.2. Procesamiento paralelo en GPUs Una Unidad de Procesamiento Gráfico o GPU es un coprocesador diseñado inicialmente para el procesamiento de gráficos principalmente cuya misión es liberar a la CPU en aplicaciones de uso intensivo de gráficos como juegos y otras aplicaciones 3D interactivas. Recientemente, estos dispositivos han dejado de ser considerados de uso exclusivo para procesamiento gráfico y están siendo utilizados para computación de propósito general propiciado principalmente por la aparición de herramientas y librerı́as que simplifican el desarrollo de programas que explotan las capacidades de cálculo de las GPUs. En la actualidad, existen dos fabricantes principales de tarjetas GPUs, a saber, Nvidia y AMD, pero es el primero el que está liderando el mercado de las GPUs aplicadas a la computación de altas prestaciones gracias al framework Compute Unified Device Architecture (CUDA), que facilita el desarrollo de aplicaciones que explotan las capacidades de cómputo de sus productos. Por este motivo cuando en este apartado se haga referencia a una GPU, se referirá a una GPU de Nvidia que soporte CUDA. Una GPU se compone de un conjunto de multiprocesadores que podrı́an clasificarse dentro de los de tipo SIMD (Single Instruction Multiple Data) [12]. Cada uno de los multiprocesadores que compone la GPU cuenta con los siguientes elementos de memoria (ver figura 3.2): Un conjunto de registros por procesador. 1 http://meetings.mpi-forum.org/MPI 3.0 main page.php 3.2. PROCESAMIENTO PARALELO EN GPUS 17 Figura 3.2: Esquema hardware de una GPU Una memoria compartida o caché paralela, que es compartida por todos los procesadores. Una caché de constantes y otra de texturas, ambas de solo lectura. La diferencia fundamental entre una GPU y una CPU radica en que la primera invierte más transistores para el procesamiento de datos que para caché y control de flujo que la segunda, como puede verse en la figura 3.3. Esto significa que una GPU está preparada especialmente para afrontar problemas que pueden resolverse de forma paralela, y necesita muy poco control de flujo ya que estos problemas se basan fundamentalmente en la ejecución de un mismo programa a cada elemento de un conjunto de datos. Cabe destacar que a medida que van evolucionando GPUs y CPUs, tanto la capacidad de procesamiento como el ancho de banda de las primeras crece de forma exponencial (ver figuras 3.4 y 3.5). Figura 3.3: Arquitectura GPU frente a CPU Aun ası́, la comparación directa de rendimiento CPU y GPU no es del todo correcta dado que las mediciones se basan en la ejecución de benchmarks de programas especı́ficos. Las GPUs, al ser un hardware especializado, no implementan operaciones ni instrucciones de propósito general que implementan las CPUs y no están destinadas a sustituir a éstas en ningún momento. En el siguiente apartado se describen las caracterı́sticas fundamentales de Nvidia CUDA, utilizado para todos los desarrollos para GPU que se han realizado en el marco de este trabajo. 18 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS Figura 3.4: Operaciones en coma flotante CPU frente a GPU Figura 3.5: Ancho de banda de memoria en GB/s de CPU frente a GPU 3.2.1. NvidiaTM CUDA CUDA son las siglas de Compute Unified Device Architecture y hace referencia tanto a un compilador como a un conjunto de herramientas de desarrollo creadas por Nvidia que permiten a los programadores usar una variación del lenguaje de programación C para aprovechar la potencia de computación masiva en paralelo de las GPUs de Nvidia para propósito general. CUDA presenta un nuevo modelo de programación en paralelo que permite resolver problemas complejos de forma más eficiente que si se tratase de una CPU. Una de las singularidades es que utiliza un modelo de computación paralela donde cada uno de los procesadores ejecuta un mismo bloque de código o kernel sobre diferentes datos en paralelo. CUDA consigue el paralelismo a través de tres elementos estructurados y anidados: hilos, bloques y grids. Los hilos se agrupan en bloques y los bloques en grids (ver figura 3.6). Un hilo o thread es la unidad más básica e indivisible de paralelismo en CUDA. Estos elementos son utilizados para ejecutar bloques de código organizados según el criterio del desarrollador, permitiendo elegir una configuración adecuada para maximizar el rendimiento. La GPU permite definir un solo grid para cada ejecución de un kernel, en el cual pueden definirse tantos bloques dentro del grid, e hilos dentro de cada bloque como se desee (teniendo en cuenta las limitaciones de la versión de la arquitectura). El número de hilos de un bloque será el mismo para la invocación de un kernel, pudiendo ser diferente para las siguientes invocaciones que vayan a ser ejecutadas. La configuración de grids y bloques se puede definir utilizando varias dimensiones, lo que permite afrontar problemas complejos de una manera más sencilla (por ejemplo, el procesamiento de un cubo de datos hiperespectrales). En cada multiprocesador de la GPU solamente puede ejecutarse un mismo bloque de código a la vez, y mientras éste no termine no podrá ejecutarse otro. Por lo tanto, es muy importante controlar qué hacen los hilos durante la ejecución y maximizar el número de hilos corriendo a la vez para obtener el mayor rendimiento posible. La diferencia de estos hilos frente a los de la CPU es que 3.2. PROCESAMIENTO PARALELO EN GPUS 19 Figura 3.6: Esquema del modelo de programación CUDA son extremadamente ligeros y operaciones como su creación y el cambio de contexto tienen un coste insignificante. Las GPUs pueden manejar miles de hilos frente a los procesadores convencionales, que solo pueden manejar unos pocos. Como cada hilo de un bloque ejecuta el mismo bloque de código, el identificador de éstos puede ser utilizado para realizar parte del control de flujo y para calcular la dirección de memoria de inicio de los datos a procesar. Éste es el motivo por el que CUDA facilita de manera notable la realización de cálculos vectoriales y matriciales, muy utilizados en los algoritmos de procesamiento de imágenes hiperespectrales. Otra caracterı́stica de CUDA es que permite la compartición de datos entre los hilos de un mismo bloque para evitar cálculos redundantes y compartir accesos a memoria con el fin de aumentar el ancho de banda en dichos accesos. Estas caracterı́sticas vienen dadas principalmente por el modelo de memoria. El modelo de memoria de CUDA (ver figura 3.7) define una jerarquı́a de memoria para el acceso a los datos. Siguiendo los elementos descritos en el modelo de programación, cada hilo posee su propia memoria privada, de tiempo de acceso muy rápido. Por otro lado, cada bloque tiene acceso a su propia memoria, que es compartida y está disponible para todos los hilos que componen el bloque. El tiempo de vida de la memoria de hilo y de bloque es el mismo que la del elemento en sı́, es decir, cuando el elemento termine su ejecución ésta dejará de estar disponible. En el nivel más alto de la jerarquı́a se encuentra la memoria global, compartida entre todos lo hilos en ejecución en la GPU. Además, existen dos tipos de memoria adicionales de solo lectura: la memoria de texturas y la memoria de constantes. Estas dos memorias son globales a todos los hilos y están optimizadas para utilizarse con tipos de datos especı́ficos. Estas tres memorias (global, constantes y texturas) tienen el mismo tiempo de vida que la aplicación que las invoca, perdurando entre diferentes invocaciones de kernels dentro de la misma ejecución de la aplicación. Destacar que desde la aparición de la gama de GPUs Fermi, se proporciona un nuevo nivel de memoria basado en cachés. Cada hilo, además de tener acceso a su memoria privada y a la memoria compartida de bloque, tiene acceso a una caché de nivel 1 (L1) con velocidad de acceso similar a la memoria compartida. De hecho, CUDA proporciona una función (cudaFuncSetCacheConfig) que permite al desarrollador aumentar para cada kernel el tamaño de la memoria caché L1 en detrimento del tamaño de la memoria compartida o viceversa. Por otro lado también se implementa un segundo nivel de memoria caché (L2), de 768KB de tamaño para los chips de 512 cores, que cubre tanto la memoria global de la GPU como la memoria del sistema. Una de las caracterı́sticas que presenta este sistema de caché L2 que no está presente en las CPUs es la inclusión de un conjunto de instrucciones de lectura-modificación-escritura de memoria que son atómicas y, por lo tanto, muy adecuadas para 20 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS Figura 3.7: Esquema del modelo de memoria CUDA gestionar el acceso a datos compartidos entre los diferentes bloques o incluso kernels. La finalidad principal de las cachés L1 y L2 es ayudar a mejorar el rendimiento de los accesos aleatorios a memoria. 3.2.2. Escritura de programas en CUDA Una caracterı́stica importante del desarrollo de programas para GPUs es que tienen que desarrollarse en CUDA. Al ser una variación del lenguaje C no resulta especialmente complicado para un desarrollador con experiencia en este lenguaje. El desarrollo de programas en CUDA se realiza principalmente utilizando un lenguaje de alto nivel como C o C++ (se pueden utilizar otros ya que existen wrappers especı́ficos para otros lenguajes) e implementando las funciones que se deseen ejecutar en la GPU utilizando las extensiones proporcionadas por CUDA. A continuación se describen los tipos de datos y funciones, variables predefinidas y funciones más comúnmente utilizadas en el desarrollo de estos programas. Declaración de funciones: • device : Indica que una función será ejecutada por la GPU e invocada desde un kernel. • global : Indica que una función será ejecutada por la GPU y será llamada por el código secuencial. Es el modificador utilizado en la declaración de un kernel. • host : Indica que una función será ejecutada por la CPU y será llamada por el código secuencial. Es el modificador aplicado por omisión. Declaración de variables: • device : La variable residirá en memoria global, siendo accesible por todos los threads de cualquier grid durante el tiempo de vida de la aplicación. • constant : La variable residirá en memoria constante, siendo accesible por todos los threads de cualquier grid durante el tiempo de vida de la aplicación. Es de solo lectura. • shared : La variable residirá en memoria compartida y será accesible por todos los hilos de un bloque durante el tiempo de vida del mismo. Variables predefinidas: • dim3 gridDim: Dimensiones de un grid. Un grid puede tener 3 dimensiones como máximo (x, y y z). En las GPUs actuales, el máximo valor para una dimensión es 65.535 por lo que el número máximo de bloques en una grid será 65535 × 65535 × 65535. • uint3 blockIdx: Índice de un bloque dentro de un grid. Almacena los valores para las 3 dimensiones en los atributos x, y y z. 3.3. EL CLÚSTER DE GPUS DE CETA-CIEMAT 21 • dim3 blockDim: Dimensiones de un bloque. Al igual que los grids, pueden tener hasta 3 dimensiones. Un bloque puede tener 1.024 hilos como máximo (x × y × z ≤ 1024, z ≤ 64). • uint3 threadIdx: Índice de un hilo dentro de un bloque. Almacena los valores para las 3 dimensiones en los atributos x, y y z. • int warpSize: Número de hilos en un warp. Un warp es un grupo de hilos paralelos que controla un multiprocesador de una GPU. Un warp tiene 32 hilos. Algunas funciones básicas: • cudaMalloc: Reserva memoria en la GPU. Es similar a la función malloc de C para la CPU. • cudaFree: Libera la memoria de la GPU reservada para una dirección de memoria dada. Es similar a la función free de C para la CPU. Si no se libera memoria entre ejecuciones de kernels el espacio permanece ocupado. • cudaMemcpy: Permite transferir memoria de manera bloqueante. Principalmente se utiliza para transferencias de host a GPU y viceversa, aunque se puede utilizar de host a host para, por ejemplo, copiar memoria a un área no paginable. • cudaMemcpyAsync: Similar a la función cudaMemcpy pero no es bloqueante. • syncthreads: Sirve para establecer un punto de sincronización (barrera) entre los hilos de un bloque. La mayorı́a de estas funciones y variables se han utilizado en la implementación paralela del algoritmo PPI para clúster de GPUs desarrollada bajo el marco de este trabajo. En el apartado siguiente, se describe la infraestructura utilizada parar el desarrollo y la ejecución de las pruebas; el clúster de GPUs de CETA-CIEMAT. 3.3. El clúster de GPUs de CETA-CIEMAT Tras la introducción realizada en los apartados anteriores a computación clúster y GPU, en este apartado se describe una arquitectura de computación de reciente aparición que surge de la hibridación de ambas soluciones de cómputo: el clúster de GPUs. Un clúster de GPUs es el resultado de equipar cada nodo de cómputo de un clúster de computadores con una o varias GPU. Esta nueva arquitectura pretende aprovechar las altas capacidades de cómputo de las tarjetas GPU para incrementar el rendimiento global de una infraestructura de tipo clúster. La incorporación de este tipo de dispositivos es muy sencilla y puede realizarse sin que ello suponga una gran inversión económica, pudiendo incluso ser incorporadas a una infraestructura clúster ya existente. Tal es la potencia de cálculo alcanzada por este tipo de arquitectura de cómputo que ya copan algunas de las posiciones privilegiadas dentro del ránking de supercomputadores Top5002 . En el momento de la escritura de esta memoria, el primer clúster de GPUs que podemos encontrar es el Tianhe-1A (ver figura 3.8), en la posición 5, localizado en el National Supercomputing Center in Tianjin (China). En el momento de su puesta en marcha (Octubre de 2010) llegó a alcanzar el número 1 del Top500. Este supercomputador se compone de 14.336 nodos de CPU y 7.168 Nvidia Tesla M2050, llegando a alcanzar los 2,507 Petaflops. En la actualidad, se utiliza para simulación aérea y extracción de petróleo entre otros usos. En lo que a España respecta, el clúster de GPUs más potente que existe a dı́a de hoy es MinoTauro (ver figura 3.9), ubicado actualmente en el Centro Nacional de Análisis Genómico (CNAG) de Barcelona y perteneciente al Barcelona Supercomputing Center (BSC). Está formado por 128 CPUs de seis núcleos y 64 tarjetas GPU Nvidia Tesla M2090. Actualmente es el supercomputador más eficiente de Europa. Otra instalación española a destacar es el clúster de GPUs del Centro Extremeño de Tecnologı́as Avanzadas (CETA), ubicado en Trujillo (Cáceres). El CETA es una subsede del Centro de Investigaciones Energéticas, Medioambientales y Tecnológicas (CIEMAT) dedicado a la investigación, desarrollo y servicio en tecnologı́as de la información y de las comunicaciones en beneficio de la ciencia, la industria y la sociedad en general, en los ámbitos extremeño, español, europeo y latinoamericano. CIEMAT es un Organismo Público de Investigación adscrito al Ministerio de Economı́a y Competitividad para la 2 http://www.top500.org/ 3 Fuente: http://goo.gl/M0fdo 22 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS Figura 3.8: Supercomputador Tianhe-1A3 Figura 3.9: Clúster de GPUs MinoTauro del BSC4 investigación de excelencia en materias de energı́a y medio ambiente, ası́ como en múltiples tecnologı́as de vanguardia y en algunas áreas de investigación básica. El clúster de GPUs de CETA se compone de dos subsclústers de GPUs (llamados comúnmente clúster Tesla y clúster Fermi), donde cada uno cuenta con GPUs pertenencientes a las generaciones Tesla y Fermi de NVIDIA. El clúster Tesla está compuesto de 17 nodos de cómputo y está destinado al desarrollo y pruebas de programas, ası́ como para el aprendizaje de uso del clúster. Por otro lado, el clúster Fermi está compuesto de 32 nodos de cómputo y está dedicado a la producción cientı́fica. Ambos entornos usan una red de comunicaciones de tipo Infiniband. El clúster Tesla también es denominado entorno de Test y el clúster Fermi entorno de Producción. Las figuras 3.11 y 3.12 muestran en detalle los elementos del clúster y su disposición fı́sica. Cada nodo del clúster está compuesto del siguiente equipamiento: 2 x Quad Core Intel Xeon 2,26 GHz. 24 GB SDRAM DDR3 1333MHz ECC. 500 GB SATA. 2 Nvidia Tesla C1060 (mitad de un TESLA S1070), para los nodos del entorno de Test y 2 Nvidia Tesla C2050 (mitad de un S2050) o 2 Nvidia Tesla C2070 (mitad de un S2070) para los nodos del entorno de Producción. Para la gestión y planificación de los trabajos que se envı́an al clúster se utiliza el software Simple Linux Utility for Resource Management (SLURM). SLURM es un software de fuentes abiertas, tolerante a fallos y de alta escalabilidad orientado a clústers de computadores basados en Linux. Define un conjunto de colas o entornos que agrupan los recursos del clúster (ver figura 3.10), permitiendo la asignación de diferentes permisos y prioridades por cada entorno a los usuarios del clúster. 4 Fuente: http://www.bsc.es/plantilla.php?cat id=44 3.3. EL CLÚSTER DE GPUS DE CETA-CIEMAT 23 Figura 3.10: Arquitectura del entorno HPC de CETA-CIEMAT (Imagen cedida por el Dr. Abel F. Paz, Responsable de Infraestructura de CETA-CIEMAT) El clúster de GPUS de CETA-CIEMAT es un clúster semi-homogéneo, ya que todos los nodos de cómputo funcionan con el mismo sistema operativo (CentoOS 6.2 de 64 bits). El espacio de almacenamiento de usuario está basado en el sistema de ficheros distribuido LUSTRE. Soporta el uso de MPI, utilizando el sabor MVAPICH 1.8 (implementación de MPI optimizada para redes Infiniband) y CUDA en su versión 4.1. Los usuarios acceden al clúster a través de una máquina de login (loginhpc.ceta-ciemat.es), que contiene una gran variedad de compiladores ası́ como librerı́as de cálculo cienfı́fico. Desde este nodo se compilan los programas y se envı́an al clúster para su ejecución. Actualmente el clúster está siendo utilizado por diferentes grupos de investigación nacionales e internacionales de diferentes disciplinas como Genómica, Fı́sica de Altas Energı́as, Dinámica de Fluidos y Análisis de Imágenes Hiperespectrales, entre otras. En esta lı́nea de investigación, hay que comentar 24 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS que el desarrollo y pruebas de las diferentes versiones paralelas del algoritmo PPI para clúster de GPUs se han llevado a cabo satisfactoriamente utilizando la infraestructura de CETA-CIEMAT. Figura 3.11: Disposición de los elementos hardware del clúster Tesla de GPUs de CETA-CIEMAT (Imagen cedida por el Dr. Abel F. Paz, Responsable de Infraestructura de CETA-CIEMAT) 3.3. EL CLÚSTER DE GPUS DE CETA-CIEMAT 25 Figura 3.12: Disposición de los elementos hardware del clúster Fermi de GPUs de CETA-CIEMAT (Imagen cedida por el Dr. Abel F. Paz, Responsable de Infraestructura de CETA-CIEMAT) 26 CAPÍTULO 3. ARQUITECTURAS PARALELAS CONSIDERADAS Capı́tulo 4 Método En este capı́tulo se describe el algoritmo Pixel Purity Index también conocido como PPI. Este algoritmo es un conocido método de extracción de endmembers en imágenes hiperespectrales y multiespectrales. Este algoritmo ha sido seleccionado para este trabajo por su sencillez de implementación y su elevado coste computacional. En capı́tulos posteriores se describen las diferentes implementaciones paralelas para clúster de computadores, tarjetas GPU y clúster de GPUs utilizadas para el análisis de rendimiento. 4.1. Pixel Purity Index El algoritmo Pixel Purity Index o PPI es uno de los algoritmos de selección de pı́xeles puros o endmembers más conocidos. El algoritmo PPI [7] fue desarrollado por Boardman, Kruse y Green en 1993 y posteriormente fue incorporado al software ENVI de Exelis Visual Information Solutions (inicialmente desarrollado por Better Solutions Consulting, LLC). El algoritmo parte de la generación de un conjunto amplio de vectores aleatorios N-dimensionales llamados skewers. Cada punto de la imagen hiperespectral se proyecta en cada skewer y aquellos pı́xeles que correspondan a los extremos en la dirección de dicho skewer son selecionados como candidatos a firma espectral pura. Según crece el tamaño del conjunto de skewers, el número de pı́xeles seleccionados también aumenta y algunos de los pı́xeles seleccionados previamente vuelven a serlo. Finalizado el proceso para cada skewer, aquellos pı́xeles que han sido más veces seleccionados como candidatos, o dicho en otras palabras, aquellos con mayor ı́ndice de pureza, son considerados como firmas espectrales puras. PPI pertenece al conjunto de los métodos interactivos, es decir, que necesita intervención del usuario, y es el más representativo. Como se ha indicado anteriormente, su objetivo es localizar aquellos puntos de la imagen que son espectralmente más puros. Se basa en la suposición de que los puntos extremos del conjunto de pı́xeles son los mejores candidatos para ser utilizados como endmembers. Los parámetros del algoritmo son los siguientes: Una imagen multiespectral o hiperespectral a procesar. El número de iteraciones a realizar, esto es, el tamaño del conjunto de skewers a generar. Un valor umbral para seleccionar los pı́xeles puros. Los siguientes pasos describen el funcionamiento del algoritmo: 1. Inicialmente, el algoritmo asigna un ı́ndice de pureza a todos los pı́xeles de la imagen. El contador de selección de candidato como endmember se inicializa a 0 para cada pı́xel. 2. Posteriormente se genera un skewer o divisor. Es un vector unitario aleatorio cuyo objetivo es particionar el conjunto de pı́xeles de la imagen. 3. Una vez generado el skewer, se proyectan todos los pı́xeles de la imagen sobre éste, identificando los puntos extremos en la dirección definida por el vector unitario. El ı́ndice de pureza de los pı́xeles seleccionados se incrementa en 1. 27 28 CAPÍTULO 4. MÉTODO 4. Se repiten los pasos del punto 2 al punto 3 tantas veces como indique el parámetro de entrada número de iteraciones. 5. Una vez ejecutadas todas las iteraciones, se obtiene como resultado una imagen de pureza formada por los ı́ndices asociados a cada pı́xel de la imagen. Figura 4.1: Selección de los pı́xeles extremos para un conjunto de skewers en el algoritmo PPI 6. En este momento, se aplica el parámetro de entrada valor umbral, seleccionando aquellos pı́xeles de la imagen cuyo ı́ndice de pureza asociado es mayor que el valor umbral. Estos pı́xeles seleccionados son etiquetados como puros. La figura 4.1 muestra la selección de los pı́xeles extremos para un caso sencillo con tres skewers y seleccionando aquellos pı́xeles cuyo ı́ndice de pureza es mayor que 0. 7. Los pı́xeles seleccionados se cargan en una herramienta intereactiva llamada N-dimensional Visualizer, incluı́da en ENVI, la cual permite realizar diagramas de dispersión de los primeros autovectores obtenidos tras la aplicación de una reducción de dimensionalidad (transformación MNF) sobre los datos originales. 8. Finalmente, utilizando el N-dimensional Visualizer, el usuario escoge manualmente aquellos pı́xeles o grupos de pı́xeles de la imagen que aparecen como extremos en proyecciones sucesivas, identificando un subconjunto de ellos que se etiquetarán como endmembers. En el caso de seleccionar un grupo de pı́xeles, el valor del endmember se calculará a partir del espectro medio. Una descripción analı́tica del algoritmo se describe a continuación [28]. Las entradas se definirı́an como: Un cubo de datos multiespectral o hiperespectral con N dimensiones. El tamaño del conjunto de vectores aleatorios que se van a generar durante la ejecución K. Un valor umbral de corte tv , utilizado para seleccionar como endmembers aquellos pı́xeles que han sido seleccionados como extremos al menos tv veces durante la ejecución del algoritmo. Y los pasos del algoritmo quedarı́an de la siguiente manera: 1. Generación de skewers: Produce un conjunto de K vectores generados aleatoriamente {skewerj }K j=1 2. Proyecciones de los extremos : Para cada skewerj , toda la muestra de pı́xeles fi del conjunto de datos original F se proyecta sobre el skewerj mendiante el cálculo del producto escalar para encontrar los pı́xeles de muestra que son extremos (máximo y mı́nimo). De esta forma se obtiene un conjunto de extremos para el skewerj que se denota por Sextrema (skewerj ). El producto escalar se calcula de la forma |fi · skewerj |. 4.1. PIXEL PURITY INDEX 29 A pesar del hecho de que diferentes skewers skeweri y skewerj pueden generar conjuntos de extremos diferentes Sextrema (skeweri ) y Sextrema (skewerj ), es muy probable que una misma muestra de vectores aparezca en más de un conjunto de extremos. Para hacer frente a este situación se define la función lS (x) la cual denota la pertenencia de un elemento x para un conjunto particular S. Esta función se define por la función 4.1 ( lS (fi ) = 1 0 si x ∈ S si x ∈ /S (4.1) 3. Cálculo de las puntuaciones PPI: Utilizando la función 4.2, se calcula la puntuación PPI asociada a un vector de la muestra fi (número de veces que un pixel dado ha sido seleccionado como extremo) NP P I (fi ) = ΣK j=1 ISextrema (skewer)j (fi ) (4.2) La parte del algoritmo PPI que es más costosa computacionalmente hablando es la descrita en el paso 2 (Proyecciones de los extremos), pero es muy adecuada para ser paralelizada, ya que no hay dependencia de datos entre iteraciones. Por último, se ha de comentar que el algoritmo PPI contiene etapas totalmente automatizadas, como la fase de generación de la imagen de pureza, pero se hace necesaria una etapa interactiva final en la que el usuario debe seleccionar manualmente aquellos pı́xeles que quiere utilizar como endmembers. El usuario no conoce de antemano cuál es el número apropiado de endmembers a seleccionar, por lo que el conocimiento a priori de la imagen es muy conveniente. Este hecho y la aleatoriedad de la generación de los skewers representan los principales inconvenientes de esta metodologı́a [22]. En el siguiente capı́tulo se describen las diferentes implementaciones paralelas del algoritmo PPI desarrolladas para un clúster de computadores, tarjetas GPUs y para clúster de computadores equipados con tarjetas GPUs. 30 CAPÍTULO 4. MÉTODO Capı́tulo 5 Implementaciones paralelas En este capı́tulo se presentan las diferentes versiones de implementaciones paralelas desarrolladas para el algoritmo de extracción de firmas espectrales puras Pixel Purity Index descrito en el capı́tulo anterior. Primeramente se describe la implementación paralela desarrollada para un clúster de computadores con el fin de acelerar la versión secuencial de dicho algoritmo. Además, se describe el problema del particionamiento de datos y el paralelismo adoptado, presentando algunos ejemplos prácticos que permiten analizar los pasos seguidos en el proceso de paralelización. A continuación, se explica la versión paralela de PPI para GPUs, comentando las funciones que se ejecutan en el dispositivo o kernels desarrollados y el esquema de pararelismo adoptado. Para finalizar se presenta la implementación desarrollada para el clúster de GPUs de CETA-CIEMAT, un clúster de computadores que contiene dos tarjetas GPU por nodo de cómputo. 5.1. Implementación de PPI para un clúster de computadores En este apartado se analizan la implementación de PPI realizada para clúster de computadores, prestando especial atención al esquema de paralelismo utilizado y a la comunicación entre los nodos de cómputo. Además, debido a que los algoritmos de análisis hiperespectral para esta arquitectura paralela se basan generalmente en el reparto de los datos a procesar entre los nodos del clúster, se presentan las diferentes estrategias de particionamiento que pueden adoptarse, destacando sus ventajas e inconvenientes. 5.1.1. Estrategias de particionamiento de datos hiperespectrales El problema de particionamiento de datos hiperespectrales para procesamiento en paralelo se ha explorado en diferentes aproximaciones en la literatura reciente [26]. En la mayor parte de los casos, el particionamiento de datos está asociado a un patrón de algoritmo paralelo maestro-esclavo o masterworker, en la que un proceso, el maestro o master, se encarga de realizar el particionamiento de los datos hiperespectrales y repartirlos entre el resto, los esclavos o slaves, los cuales se encargan de procesarlos. Una vez que el esclavo finaliza su tarea, genera un resultado parcial que envı́a de vuelta al maestro. Cuando éste ha recopilado los datos parciales de cada uno de los esclavos, se encarga de procesarlos y combinarlos para generar un resultado global. Teniendo en cuenta este patrón de procesamiento, a continuación se describen tres tipos de particionamiento de datos considerados en el presente trabajo a la vez que se presentan sus ventajas e incovenientes. Para finalizar se justifica la elección de uno de los tipos propuestos como modelo base para el desarrollo de las implementaciones paralelas descritas a lo largo del capı́tulo. 1. Particionamiento espectral. Este tipo de particionamiento divide el cubo de datos hiperespectrales en subvolúmenes, cada uno de los cuales resulta de seccionar el cubo con planos paralelos al plano formado por los ejes X e Y , correspondientes a lı́neas y muestras respectivamente. Esto provoca que cada subvolumen contenga información de todos los pı́xeles de la imagen, de forma que la información espectral asociada a un mismo pı́xel puede encontrarse almacenada en dos o más procesos o nodos [ver figura 5.1 (a)]. Aplicando este tipo de particionamiento, el procesamiento de un único pı́xel de la imagen requiere la comunicación entre los procesos mencionados debido a que la información espectral se encuentra dispersa entre varios procesadores de la arquitectura. 31 32 CAPÍTULO 5. IMPLEMENTACIONES PARALELAS Figura 5.1: Estrategias de particionamiento de imágenes hiperespectrales. (a) Particionamiento espectral. (b) Particionamiento espacial. (c) Particionamiento hı́brido. 2. Particionamiento espacial. Este tipo de particionamiento divide el cubo de datos hiperespectrales en bloques de pı́xeles, cada uno de los cuales resulta de seccionar el cubo con planos paralelos al plano formado por los ejes X y Z, correspondientes a lı́neas y bandas respectivamente. Esto provoca que cada bloque contenga toda la información espectral relativa a un subconjunto de pı́xeles de la imagen contiguos en el dominio espacial [ver figura 5.1 (b)]. Aplicando este tipo de particionamiento, el procesamiento de un único pı́xel de la imagen no requiere comunicación entre procesos debido a que todos los valores espectrales se encuentran localizados en un mismo procesador de la arquitectura. 3. Particionamiento hı́brido. Este tipo de particionamiento resulta de aplicar conjuntamente los dos tipos de particionamiento anteriores: el cubo de datos hiperespectrales queda dividido en subvolúmenes, cada uno de ellos integrando una partición de la imagen en el dominio espacial y a su vez la información espectral asociada a un pı́xel de dicho subvolumen puede encontrarse dispersa en dos o más procesadores (ver figura 5.1 (c)). Dada las caracterı́stidas del algoritmo PPI y para aprovechar su paralelismo inherente, se ha optado por un particionamiento de datos de tipo espacial, debido principalemente a los siguientes motivos: Primeramente, el particionamiento espacial se adapta de manera natural a la paralelización de los algoritmos de procesamiento basados en ventana deslizante, al almacenarse en una misma unidad de proceso todos los valores espectrales de pı́xeles contiguos en el dominio espacial. Otra razón fundamental para seleccionar este tipo de división de los datos espectrales viene dada por la forma de trabajar del algoritmo PPI. Tal y como se ha descrito en el capı́tulo anterior, el algoritmo calcula en cada iteración el valor de la proyección de cada pı́xel de la imagen sobre un vector unitario y aleatorio o skewer. El particionamiento espacial permite reducir sensiblemente las comunicaciones entre los diferentes procesos o nodos de la arquitectura al encontrarse almacenados todos los valores espectrales para un pı́xel dado en la misma unidad de proceso. El particionamiento espectral o hı́brido involucra comunicaciones inter-proceso a nivel de pı́xel, incrementando significativamente el coste total asociado a las comunicaciones, lo que se traduce en un menor rendimiento en paralelo y en problemas de escalabilidad según aumenta el número de nodos de cálculo de la arquitectura. Para terminar, una razón fundamental para la selección del particionamiento espacial es la posibilidad de reutilizar el código y de mejorar la portabilidad de los algoritmos paralelos implementados a diferentes arquitecturas. En la mayor parte de las aplicaciones, es altamente deseable poder reutilizar los códigos serie a la hora de desarrollar versiones paralelas, debido principalmente a la complejidad de algunas técnicas de análisis. Por ello, el particionamiento espacial permite un paralelismo de grano fino al facilitar la aplicación de un algoritmo paralelo a diferentes porciones de datos que contienen toda la información espectral, permitiendo que la 5.1. IMPLEMENTACIÓN DE PPI PARA UN CLÚSTER DE COMPUTADORES 33 transformación del código serie a paralelo sea mucho más sencilla que aplicando otro de los tipos de particionamiento descritos anteriormente. En el siguiente apartado se explica la aplicación del particionamiento espacial como parte de la paralelización del algoritmo PPI. 5.1.2. Detalle de la implementación La implementación paralela del algoritmo PPI para clúster de computadores implementa el patrón maestro-esclavo de comunicación entre los procesos explicado anteriormente, paralelizando el procesamiento de la imagen entre los nodos esclavos para disminuir el tiempo de ejecución del algoritmo. El maestro realiza la división de la imagen aplicando un particionamiento de espacial de datos, de tal forma que todos los valores espectrales de los pı́xeles de cada fragmento asignado se encuentren en un mismo nodo esclavo y se evite establecer una comunicación con otro nodo, lo que penalizarı́a en el tiempo de ejecución. Los parámetros del algoritmo son los mismos que los de la versión secuencial descrita en el apartado 4.1: Una imagen hiperespectral de entrada F . El tamaño del conjunto de vectores unitarios aleatorios o skewers K a generar. La salida del algoritmo es una imagen resultado R que almacena en cada coordenada espacial R(x, y) el número de veces que dicho pı́xel ha sido seleccionado como posible endmember por el algoritmo. A continuación se expone paso por paso de esta versión paralela del algoritmo PPI: 1. El procesador maestro lee el fichero de cabecera en formato ENVI header format (HDR) que describe el fichero que contiene la imagen hiperespectral F . De este fichero obtiene la siguiente información: num bands: Número de bandas que contiene la imagen de entrada. num lines: Número de lı́neas que contiene la imagen de entrada para cada banda. num samples: Número de muestras (pı́xeles) por cada lı́nea de la imagen de entrada. data type: Parámetro que identifica el tipo de representación. Están soportados los valores 1 (entero de 16 bits), 4 (coma flotante de 32 bits) y 5 (coma flotante 64 bits). byte order: Describe el orden de los bytes en los tipos de datos. 0 describe la codificación Least Significant Byte First o LSF (sistemas DEC y Microsoft) y 1 describe la codificación Most Significant Byte First o MSF (resto de sistemas - SUN, SGI, IBM, HP, etc.). Una vez leı́do el fichero de cabecera, el nodo maestro lee el fichero que contiene la imagen hiperespectral e intenta distribuirlos de forma balanceada entre los nodos esclavos (él mismo incluı́do). Para ello se calcula el resto de la división entre el número de lı́neas de la imagen (num lines) y el número de procesos (num processes) (ver fórmula 5.1). c = num lines % num processes (5.1) Si el resultado no es 0, se reparten las lı́neas entre los primeros c procesos. 2. El proceso maestro genera un número de vectores unitarios aleatorios del tamaño del parámetro de entrada s. Cada vector tendrá un tamaño del número de bandas num bandas de la imagen de entrada. Para la generación de los vectores aleatorios se utiliza el generador de números aleatorios Mersenne-Twister [25] distribuido en la librerı́a GNU Scientific Library (GSL) [13]. El código 5.1 muestra cómo se generan los vectores aleatorios. 34 CAPÍTULO 5. IMPLEMENTACIONES PARALELAS gsl_rng * r ; if (( r = gsl_rng_alloc ( gs l_ r ng _m t1 9 93 7 ) ) == NULL ) { printf ( " ERROR : Could not create random number generator \ n " ) ; exit (1) ; } // Set seed gsl_rng_set (r , time ( NULL ) * getpid () ) ; for ( i = 0; i < num_skewers ; i ++) { for ( j = 0; j < num_bands ; j ++) { skewers_data [ i * num_bands + j ] = g s l _ r n g _ u n i f o r m _ i n t (r , SHRT_MAX ) ; } } gsl_rng_free ( r ) ; Código 5.1: Generación aleatoria de los skewers Una vez generado el conjunto de skewers, se realiza una operación de distribución o broadcast a todos los procesos esclavos para que tengan el conjunto de skewers generado. El código 5.2 muestra el código MPI utilizado para la distribución de los skewers. MPI_Bcast ( skewers_data , skewers_data_size , MPI_FLOAT , 0 , MPI_C OMM_WORL D ) ; Código 5.2: Broadcasting de los skewers 3. En este momento todos los procesos tienen los datos necesarios para ejecutar el algoritmo PPI localmente. El código del algoritmo es el mismo código que el código secuencial, pero utilizando como imagen fuente el bloque de la imagen recibido. Una vez que se ha ejecutado el algoritmo, el proceso envı́a de vuelta al proceso maestro el resultado parcial obtenido tras ejecutar el algoritmo PPI. El nodo maestro, una vez ejecutado el algoritmo PPI localmente (también actúa como esclavo) efectúa una operación de recolección de resultados (gathering). El cuadro de código 5.3 ejemplifica la función MPI utilizada para el envı́o del resultado al proceso maestro. MPI_Gather ( local_results , num_skewers * 6 , MPI_FLOAT , gathered_results , num_skewers * 6 , MPI_FLOAT , 0 , MPI_C OMM_WOR LD ) ; Código 5.3: Recolección de los resultados locales 4. Una vez que el nodo maestro obtiene los resultados parciales de la ejecución del algoritmo en cada uno de los nodos esclavos se realiza el proceso de generación del resultado final del algoritmo. Para cada skewer se han de comparar los máximos y mı́nimos locales obtenidos para cada bloque de imagen distribuido. Se seleccionarán aquellos pı́xeles con el mayor pmax (x, y) y menor valor pmin (x, y). El nodo maestro generará finalmente una imagen resultado R de una sola banda y para cada máximo y mı́nimo obtenido incrementará en 1 el valor del pı́xel en la imagen resultado. Tras la ejecución del algoritmo, la imagen resultado contendrá para cada pı́xel el número de veces que ha sido seleccionado como endmember. En resumen, los pasos a seguidos en la ejecución de la implementación en paralelo del algoritmo PPI son los siguientes: 1. La imagen original se distribuye en particiones entre los diferentes procesadores. Cada procesador debe recibir un volumen de datos proporcional a sus caracterı́sticas. 2. Un nodo (maestro) se encarga del reparto de la carga y de la recolección de los resultados parciales. 3. El resto de nodos (esclavos) ejecutan el algoritmo PPI en su versión secuencial para su bloque y el conjunto de skewers recibido. La figura 5.2 representa el proceso de particionamiento de datos para PPI de forma sencilla. Esta versión del algoritmo servirá de base para la implementación paralela del algoritmo PPI para el clúster de GPUs de CETA-CIEMAT. El cuerpo del algoritmo será el mismo, conservando el esquema de particionamiento de datos (espacial) y el patrón maestro-esclavo de comunicación entre nodos. La diferencia fundamental reside en la delegación de la ejecución del algoritmo PPI a la(s) GPU(s) de cada nodo de procesamiento. La versión del algoritmo PPI que se ejecutará se describe en el apartado siguiente. 5.2. IMPLEMENTACIÓN PARALELA DE PPI PARA GPUS 35 Figura 5.2: Particionamiento de datos en el algoritmo PPI 5.2. Implementación paralela de PPI para GPUs En este apartado se describe la implementación paralela del algoritmo PPI en su versión para GPU. Esta implementación sigue un patrón diferente a la de clústers presentada en el apartado anterior, debido principalmente a que la GPU es un sistema de memoria compartida y no es necesario invertir demasiado tiempo en la comunicación entre las unidades de procesamiento. En este sentido, se muestran los detalles de esta implementación, incluyendo fragmentos de código que describen las acciones más relevantes en cada paso. Cabe destacar que en esta implementación no se implementa un patrón de comunicación maestroesclavo al uso, dado que no existe un nodo de procesamiento que reparta los datos y recolecte y procese los resultados como en la implementación anterior. Se podrı́a decir que en la implementación GPU es el proceso que se ejecuta en la CPU el que actúa de maestro, enviando a la GPU, que en este caso serı́a el esclavo, los datos para la ejecución del algoritmo. Tras su ejecución, la GPU devuelve los resultados parciales a la CPU para que ésta proceda a la generación del resultado final. Al igual que en la versión paralela de PPI para clústers, el algoritmo PPI para GPU tiene como entradas una imagen hiperespectra F y el tamaño del conjunto de skewers K a generar. A continuación se enumeran los pasos que realiza esta implementación del algoritmo: 1. El proceso de la CPU procede a la lectura de la imagen tal y como se realiza en la implementación para clústers de computadores. Tras la lectura del fichero la imagen hiperespectral se encuentra en una estructura en la memoria de la CPU y se han obtenido los datos relativos al número de bandas (num bands), número de lı́neas (num lines) y número de muestras (num samples). 2. La generación de los skewers se realiza de la misma manera que en la versión de clúster de computadores. Para la generación de los números aleatorios se utiliza la implementación del algoritmo de Mersenne-Twister que proporciona la librerı́a GSL. Este paso se podrı́a haber realizado en la GPU, utilizando la implementación de Mersenne-Twister para GPU que se porporciona en el CUDA SDK [2]. La justificación a no haberse utilizado dicha implementación se debe al hecho de poder reutilizar el mayor porcentaje de código posible de la implementación para clústers. 3. La imagen hiperespectral F se envı́a a la GPU para su almacenamiento en la memoria global. Hay que tener en cuenta que para imágenes de gran tamaño (>4GB) habrı́a que realizar un particionamiento de la imagen antes de enviarla a la GPU. Esto es debido a que las GPUs actuales tienen una capacidad limitada de memoria dedicada (4GB para la C1060 y 6GB para la C2070) y se producen errores al intentar reservar un espacio de memoria tan grande con cudaMalloc. Tras el envı́o de la imagen se procede al envı́o a la GPU del conjunto de skewers generados. 36 CAPÍTULO 5. IMPLEMENTACIONES PARALELAS cudaMalloc (( void **) & d_image_chunk , ( chunk_size * num_samples * num_bands * sizeof ( float ) ) ) ; cudaMalloc (( void **) & d_skewers_data , ( num_skewers * num_bands * sizeof ( float ))); // host -> device copy cudaMemcpy ( d_image_chunk , image_chunk , chunk_size * num_samples * num_bands * sizeof ( float ) , c u d a M e m c p y H o s t T o D e v i c e ) ; cudaMemcpy ( d_skewers_data , skewers_data , num_skewers * num_bands * sizeof ( float ) , c u d a M e m c p y H o s t T o D e v i c e ) ; Código 5.4: Copia de la imagen hiperespectral a la memoria de la GPU El código 5.4 muestra las instrucciones CUDA para la copia de la imagen y del conjunto de skewers a la memoria global de la GPU. 4. Una vez que la GPU tiene los datos necesarios para ejecutar el algoritmo PPI, se procede a su ejecución. Para ello, cada hilo de la GPU ejecutará un fragmento de código o kernel que calculará las proyecciones de un skewer asignado a todos los pı́xeles de la imagen hiperespectral. El identificador del skewer asignado para cada hilo de ejecución vendrá dado por el código 5.5 y para su cálculo se utilizan los identificadores de bloque y de hilo dentro del bloque que proporciona la GPU. int idx = blockDim . x * blockIdx . x + threadIdx . x ; while ( idx < num_skewers ) { ... idx += num_skewers } Código 5.5: Cálculo de la selección de skewer para cada kernel Si el número de skewers es mayor al número total de hilos que se ejecutarán en la GPU, se calculará el número restante de skewers para seleccionar y los hilos con identificador más bajo ejecutarán de nuevo el kernel para el nuevo skewer. El código 5.6 muestra el código que ejecuta el cálculo del algoritmo PPI como kernel en la GPU. int idx = blockDim . x * blockIdx . x + threadIdx . x ; while ( idx < num_skewers ) { float min = FLT_MAX ; float max = FLT_MIN ; float dot_product = 0.0; for ( int k = 0; k < num_lines ; k ++) { for ( int j = 0; j < num_samples ; j ++) { dot_product = dotProduct (& skewers [ idx ] , & image [ k * num_samples * num_bands + j * num_bands ] , num_bands ) ; if ( dot_product > max ) { max = dot_product ; local_results [ idx * 6 + 0] = k ; // maxX local_results [ idx * 6 + 1] = j ; // maxY local_results [ idx * 6 + 2] = dot_product ; } if ( dot_product < min ) { min = dot_product ; local_results [ idx * 6 + 3] = k ; // minX local_results [ idx * 6 + 4] = j ; // minY local_results [ idx * 6 + 5] = dot_product ; } } } idx += num_skewers } Código 5.6: Código del kernel que implementa el algoritmo PPI para GPU 5. Tras el cálculo de los resultados del algoritmo PPI en la GPU, se devuelve a la CPU una estructura que almacena por cada skewer los pı́xeles de la imagen hiperespectral con los valores máximo y mı́nimo de la proyección. La CPU realiza un procesado final para generar la imagen resultado R de una sola banda, cuyo valor para cada pı́xel es el número de veces que dicho pı́xel 5.3. IMPLEMENTACIÓN PARALELA DE PPI PARA UN CLÚSTER DE GPUS 37 ha sido seleccionado como candidato a endmember. Este proceso es el mismo que el realizado en la versión paralela del algoritmo para clústers. Resumiendo, los pasos seguidos para la ejecución del algoritmo paralelo PPI en su versión GPU son los siguientes: 1. La imagen original se distribuye en su totalidad a la GPU. 2. La GPU actúa como esclavo ejecutando el algoritmo de forma paralela, de forma que cada hilo de ejecución calcula la proyección de todos los pı́xeles de la imagen para un skewer dado. 3. Una vez finalizada la ejecución del algoritmo en la GPU, la CPU recibe los resultados y se procesan para generar la imagen resultado. Esta versión paralela del algoritmo servirá de base, junto con la versión para clúster de computadores para la implementación de PPI para el clúster de GPU de CETA-CIEMAT. El códido del kernel que se ejecuta en la GPU será el que se ejecute en cada GPU de cada uno de los nodos de cómputo del clúster de GPUs. El siguiente apartado describe en detalle esta implementación hı́brida del algoritmo PPI. 5.3. Implementación paralela de PPI para un clúster de GPUs En los apartados anteriores hemos dado una visión detallada de las implementaciones paralelas del algoritmo PPI en sus versiones para clúster de computadores y para GPUs. Este apartado describe una implementación mixta para el clúster de GPUs de CETA-CIEMAT, descrito en el apartado 3.3 del capı́tulo 3. Recordemos que este clúster de computadores está compuesto por dos subclústers o entornos. Éstos son: Test: formado por 17 nodos de cálculo, 8 cores de CPU por nodo y 2 GPUs Nvidia Tesla C1060 (media S1070) por nodo. Producción: formado por 32 nodos de cálculo. Éstos se dividen en 16 nodos de cálculo, con 8 cores de CPU y 2 GPUs Nvidia Tesla C2050 (media S2050) por nodo y otros 16 compuestos por 12 cores de CPU y 2 GPUs Nvidia Tesla C2070 (media S2070). En esta versión del algoritmo PPI paralelo para clúster de GPUs se adopta el esquema de comunicación entre nodos maestro-esclavo implementado en la versión para clústers, con la particularidad de que no es la CPU de los nodos esclavos la que realiza la ejecución del algoritmo PPI localmente, sino que se delega la ejecución del algoritmo a la(s) GPU(s) del propio nodo. En este caso el código ejecutado en la GPU es el presentado en el apartado anterior. A continuación se enumeran los pasos de la ejecución del algoritmo para el clúster de GPUs: 1. Un nodo maestro se encarga de la lectura de la imagen hiperespectral de entrada F y del particionamiento de la misma, siguiendo el esquema de particionamiento espacial descrito en el apartado 5.1.1 de este capı́tulo, y de repartir los bloques resultantes entre el resto de nodos. En este caso el nodo maestro también procesará un bloque de la imagen de entrada para que no quede ocioso durante la ejecución local del algoritmo por los esclavos. Tal y como se hace en la versión de clúster de computadores, el reparto de los bloques es balanceado, siendo todos los bloques de tamaño similar. En esta ocasión no se ha tenido en cuenta las capacidades de cómputo de los nodos de CPU a la hora de hacer el reparto de la imagen de entrada, pues para cada entorno del clúster de GPUs de CETA-CIEMAT puede afirmarse que es un clúster homogéneo, es decir, que todos los nodos de cálculo tienen la misma capacidad de cómputo. 2. El nodo maestro genera posteriormente los vectores unitarios aleatorios (skewers) y realiza una operación de broadcasting o reparto entre el resto de nodos para que cada uno de los nodos esclavos tenga los skewers localmente. 3. Una vez que los nodos esclavos han recibido el fragmento de la imagen hiperespectral de entrada a procesar y el conjunto de skewers, ambos se envı́an a la GPU para que ésta pueda ejecutar el algoritmo PPI. El código que se ejecutará en cada una de las GPUs es el implementado en la versión paralela para GPU y se muestra en el código 5.6 del apartado 5.2 del presente capı́tulo. 38 CAPÍTULO 5. IMPLEMENTACIONES PARALELAS 4. Una vez finalizada la ejecución del algoritmo los nodos esclavos reciben los resultados generados por la(s) GPU(s). Esta salida contiene, para cada skewer del conjunto de skewers, las coordenadas espaciales (x, y) de los pı́xeles cuyos valores de proyección a cada skewer son máximo o mı́nimo junto con sus valores del proyección calculados. Además, las coordenadas son relativas al tamaño del fragmento asignado de la imagen original, dado que el nodo esclavo desconoce la distribución de bloques realizada por el maestro al resto de nodos esclavos. Como ocurre en la implementación para clústers, el nodo maestro, una vez ejecutado el algoritmo PPI localmente vı́a su(s) GPU(s) asociada(s) (recordemos que éste también actúa como esclavo) efectúa una operación de recolección de resultados o gathering. El código 5.3 del apartado 5.1.2 del presente capı́tulo muestra el código MPI utilizado para el envı́o del resultado al nodo maestro. 5. Para finalizar el nodo maestro procesa los resultados parciales recibidos de cada esclavo y selecciona para cada skewer aquellos pı́xeles con valor máximo y mı́nimo de entre los candidatos recibidos de cada nodo esclavo y se genera la imagen resultado R. Esta versión del algoritmo se ha implementado también en su versión multiGPU. En este caso, cada nodo de procesamiento recibe el bloque completo de la imagen hiperespectral a procesar, y se dividen el número de skewers entre el número de GPUs de las que conste el nodo (en el caso del clúster de CETA-CIEMAT serı́an 2). En este caso no se ha realizado otro tipo de particionamiento de los datos de entrada para evitar la comunicación entre los nodos, que sı́ implican un coste significativo si se realiza de una GPU a otra. Finalmente la CPU de cada nodo recibe los resultados de cada GPU y los agrega para construir el resultado final que se enviará al nodo maestro. Capı́tulo 6 Resultados experimentales En este capı́tulo se realiza un análisis de los resultados experimentales de las pruebas ejecutadas para las implementaciones del algoritmo PPI descritas en el capı́tulo anterior. En el primer apartado se presentan los parámetros de entrada para las pruebas realizadas en cada experimento y los entornos de pruebas. Seguidamente, se comenta la evolución de la implementación de PPI para clústers de GPUs, desde la versión inicial, que no tiene en cuenta el rendimiento, hasta la versión final, que utiliza dos tarjetas GPU simultáneamente para reducir el tiempo de ejecución de dicho algoritmo. Como cierre del capı́tulo, se realiza una comparativa entre las implementaciones secuencial, para clúster de computadores, GPUs y clúster de GPUs, analizando los resultados de cada implementación para las métricas obtenidas. 6.1. Descripción de las pruebas realizadas Este apartado presenta las pruebas realizadas a las diferentes implementaciones desarrolladas del algoritmo PPI dentro del marco de este trabajo. Primeramente se muestran los parámetros de entrada del algoritmo para todas las versiones desarrolladas y seguidamente se describen los entornos de ejecución de las pruebas realizadas. Para finalizar, se presentan las diferentes configuraciones adicionales necesarias para cada evolución concreta del algoritmo. 6.1.1. Parámetros del algoritmo PPI En el capı́tulo 4 se describe en detalle el algoritmo PPI por lo que esta sección se centrará en la enumeración de los parámetros que dicho algoritmo tienen como entrada. A saber: 1. Una imagen hiperespectral F . 2. El número de iteraciones del algoritmo o tamaño de vectores aleatorios o skewers a generar K. 3. Un valor de umbral de corte tv , utilizado para seleccionar aquellos pı́xeles de la imagen con un ı́ndice de pureza mayor que valor del umbral. Cabe comentar que para todas las pruebas se va a seleccionar un valor de umbral igual a 1. Esto quiere decir que todos los pı́xeles con ı́ndice de pureza 1, es decir, que hayan sido seleccionados al menos una sola vez como candidatos a endmember, aparecerán en la imagen resultado de la ejecución del algoritmo R. De importancia es saber también que en todas las pruebas ejecutadas se han utilizado imágenes reales obtenidas por el sensor AVIRIS, operado por el Jet Propulsion Laboratory de NASA y disponibles de manera abierta en su página web1 . Se han realizado pruebas con un total de tres imágenes de diferente tamaño que ha permitido analizar el rendimiento del algoritmo acorde al tamaño de los datos de entrada: la primera de pequeño tamaño (decenas de MBs), la segunda de un tamaño mediano (cientos de MBs) y una tercera de gran tamaño (varios GBs). Se ha considerado relevante el tamaño de la imagen hiperespectral dado que los sensores de observación remota están en continua evolución, alcanzando progresivamente resoluciones espaciales mayores y trabajando en un mayor número de bandas espectrales. 1 http://aviris.jpl.nasa.gov/alt locator/ 39 40 CAPÍTULO 6. RESULTADOS EXPERIMENTALES La primera imagen (ver figura 6.1) fue adquirida por el sensor AVIRIS en 1995 sobre la región denominada Cuprite, en el estado de Nevada, Estados Unidos. Es una imagen ampliamente utilizada en la literatura y se caracteriza porque se manifiestan singularidades que permiten discriminar entre una amplia gama de minerales calizos. La tabla 6.1 resume las caracterı́sticas de la imagen de Cuprite. Número de lı́neas Número de muestras Número de bandas Tipo de archivo Orden de bytes Tipo de dato Tamaño 350 350 188 ENVI Standard (BIP) LSF Entero 16 bits Aprox. 44 MB Tabla 6.1: Caracterı́sticas de la imagen hiperespectral AVIRIS, capturada en 1995 sobre la región minera de Cuprite, En Nevada (Estados Unidos) Figura 6.1: Imagen real AVIRIS perteneciente a la región minera de Cuprite, en Nevada (Estados Unidos) La segunda imagen utilizada fue tomada también por el sensor AVIRIS en 2008 (ver figura 6.2). Corresponde al Lago St. Clair, en Michigan (Estados Unidos), en su frontera con Canadá. Esta imagen ha sido seleccionada por la presencia de zonas de agua, vegetación y zonas habitadas. La tabla 6.2 resume las caracterı́sticas de dicha imagen. Número de lı́neas Número de muestras Número de bandas Tipo de archivo Orden de bytes Tipo de dato Tamaño 738 703 224 ENVI Standard (BIP) MSF Entero 16 bits Aprox. 222 MB Tabla 6.2: Caracterı́sticas de la imagen hiperespectral AVIRIS, capturada en 2008 sobre la región del lago St. Clair, en Michigan (Estados Unidos) La tercera imagen, también capturada por el sensor AVIRIS, pertenece al Parque Nacional de Yellowstone, en el estado de Wyoming (Estados Unidos) y data del año 2009. Esta imagen ha sido seleccionada por su gran tamaño (aprox. 4 GB). La tabla 6.3 resume las caracterı́sticas de dicha imagen. El segundo parámetro de entrada del algoritmo es el tamaño de conjunto de skewers K. Para la realización de las pruebas se ha seleccionado un valor de K igual a 15.360, partiendo de la configuración 6.1. DESCRIPCIÓN DE LAS PRUEBAS REALIZADAS 41 Figura 6.2: Imagen real AVIRIS perteneciente la región del lago St. Clair, en Michigan (Estados Unidos) Número de lı́neas Número de muestras Número de bandas Tipo de archivo Orden de bytes Tipo de dato Tamaño 11.491 777 224 ENVI Standard (BIP) MSF Entero 16 bits Aprox. 3,8 GB Tabla 6.3: Caracterı́sticas de la imagen hiperespectral AVIRIS, capturada en 2009 sobre el Parque Nacional de Yellowstone, en Wyoming (Estados Unidos) del algoritmo encontrada en anteriores trabajos seleccionados [35]. Básicamente dicho valor de K hace que encaje perfectamente con la configuración de ejecución del código que se lanzará en las GPUs y que se detallará más adelante. El uso de este valor del tamaño del conjunto de skewers junto con la imagen AVIRIS Cuprite nos permitirá comparar los resultados de rendimiento obtenidos por la implementación del algoritmo PPI para clúster de GPUs con resultados publicados anteriormente de las versiones secuencial y GPU[35, 36]. Continuando con la descripción de los parámetros de entrada de las diferentes implementaciones de PPI, adicionalmente existen parámetros especı́ficos dependientes de la arquitectura utilizada. Estos parámetros se listan a continuación: Número de nodos: Número de nodos de cómputo en los que se ejecutará en algoritmo. Este parámetro es necesario para las implementaciones para clúster y clúster de GPUs. Para las pruebas este valor variará de 1 a 15 nodos. Número de bloques: Corresponde a la cantidad de bloques de GPU a utilizar. El cálculo del número de bloques sigue la fórmula 6.1, de tal forma que cada hilo de la GPU ejecute una iteración del algoritmo. Dependiendo de la implementación de PPI y de la arquitectura (Tesla o Fermi), este número variará entre 30 y 60 para alcanzar la máxima ocupación de la GPU y ası́ maximizar el rendimiento. No de bloques = Tamaño del conjunto de skewers (K) / No de hilos por bloque Este parámetro es especı́fico de las implementaciones que utilizan GPUs. (6.1) 42 CAPÍTULO 6. RESULTADOS EXPERIMENTALES Figura 6.3: Imagen real AVIRIS perteneciente al Parque Nacional de Yellowstone, en Wyoming (Estados Unidos) Número de hilos por bloque: Este parámetro hace referencia al número de hilos por bloque de GPU. Se configura también de cara a maximizar la ocupación de la GPU y ası́ maximizar su rendimiento. En arquitecturas Tesla, como por ejemplo la del modelo C1060 Nvidia, el lı́mite está en 512 hilos por bloque. En arquitectura Fermi y posteriores, como las C2050 y C2070 de Nvidia, este número se incrementa hasta 1.024. En nuestras pruebas variará de 256 a 512, según la implementación de algoritmo. En el siguiente apartado se describen las pruebas realizadas a las diferentes implementaciones desarrolladas del algoritmo PPI para clúster de GPUs. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS 6.1.2. 43 Entornos de pruebas Tras la descripción de los parámetros de las diferentes implementaciones del algoritmo PPI sujetas a estudio, se procede a la exposición de los entornos de ejecución de las pruebas realizadas. Como se viene indicando a lo largo de esta memoria, los resultados que se presentan en este documento se han obtenido utilizando el clúster de GPUs de CETA-CIEMAT, cuya arquitectura y composición se describieron en detalle en el capı́tulo 3. Recordemos, no obstante, que el clúster de GPUs de CETA-CIEMAT se divide en dos entornos de ejecución de trabajos: Entorno de Test: Este entorno consta de 17 nodos de cómputo, cada uno de ellos equipado con 2 GPUs Nvidia C1060 (S1070). Este entorno está destinado al aprendizaje del envı́o de trabajos del clúster y las para pruebas de versiones paralelas de algoritmos en desarrollo. Entorno de Producción: Este entorno consta de 32 nodos de cómputo destinados a la producción cientı́fica. Este entorno consta de 16 nodos de cómputo equipados cada uno con 2 GPUs Nvidia C2050 (S2050) y otros 16 nodos equipados con 2 GPUs Nvidia C2070 (S2070) por nodo. Estos 32 nodos se pueden utilizar de forma conjunta o separada por nodos 2050 y 2070. Tal y como se comenta en el apartado anterior, se han realizado pruebas con configuraciones para cada entorno (Test y Producción) desde 1 a 15 nodos. El principal motivo por el que no se han realizado pruebas con un mayor número de nodos ha sido el de no acaparar el uso del clúster. Dentro de las pruebas lanzadas para cada configuración de ejecución del algoritmo se miden los siguientes tiempos: Image Retrieval Time (IRT): Tiempo de recuperación de la imagen de entrada. Skewers Generation Time (SKT): Tiempo de generación de los vectores aleatorios. Broadcasting Time (BT): Tiempo de diseminación del conjunto de skewers a los nodos esclavos. Scatter Time (ScT): Tiempo de reparto de fragmentos de la imagen de entrada entre los nodos esclavos. Computing Time (CT): Tiempo de ejecución del algoritmo en cada nodo de cálculo. Este tiempo incluye el tiempo de copiado de los datos de entradas a la GPU, el tiempo de ejecución del algoritmo en la GPU y el tiempo de recolección de los resultados. Gathering Time (GT): Tiempo de recolección de los resultados parciales de cada nodo esclavo. Post-Processing Time (PPT): Tiempo de procesamiento de los resultados parciales y de generación del resultado final del algoritmo. Total Time (TT): Tiempo total de ejecución del algoritmo. 6.2. Desarrollo de PPI para el clúster de GPUs Esta sección está destinada a la descripción de la evolución del desarrollo de la implementación paralela del algoritmo PPI parar clúster de GPUs. En total se han realizado seis implementaciones, desde la versión inicial, que no cuenta con ninguna optimización, hasta la versión final que hace uso de varias tarjetas GPU simultáneamente. 6.2.1. Implementación sin optimizaciones La primera implementación de PPI para el clúster de GPU se desarrolló con la premisa principal de tener una versión del algoritmo funcionando en un clúster de GPUs, sin tener en cuenta ninguna configuración orientada a reducir el tiempo de ejecución del algoritmo. Tal y como se comenta en el apartado 5.3 del capı́tulo 5, esta implementación parte de las implementaciones para clúster de computadores y de GPU existentes anteriormente. De la primera se ha mantenido el esquema de comunicación entre los nodos de procesamiento. Existe un proceso maestro, 44 CAPÍTULO 6. RESULTADOS EXPERIMENTALES que se encarga de la lectura de la imagen hiperespectral de entrada y de calcular el reparto balanceado de la misma entre el resto de nodos. El proceso maestro también se encarga de la generación de los skewers, que posteriormente también reparte. Una vez que cada nodo tiene los datos de entrada necesarios (fragmento de la imagen y conjunto de skewers), procede a la ejecución del algoritmo PPI localmente. Cuando el algoritmo se ha ejecutado en cada nodo, el proceso maestro recolecta los resultados parciales obtenidos por cada uno de ellos, elaborando la salida final del algoritmo. Por otro lado, la versión para GPU aporta el core del algoritmo para su ejecución en una GPU a las que tiene acceso. Cada proceso esclavo delega la ejecución del algoritmo a la GPU, quien recibe los datos de entrada, ejecuta su trabajo y devuelve los resultados para que sean enviados posteriormente al nodo maestro. El código 6.1 muestra el fragmento de código fuente que se ejecuta en la GPU. Este fragmento de código será el que evolucione entre las diferentes versiones posteriores del algoritmo, con vistas a reducir el tiempo de ejecución. int idx = blockDim . x * blockIdx . x + threadIdx . x ; while ( idx < num_skewers ) { float min = FLT_MAX ; float max = FLT_MIN ; float dot_product = 0.0; for ( int k = 0; k < num_lines ; k ++) { for ( int j = 0; j < num_samples ; j ++) { dot_product = dotProduct (& skewers [ idx * num_bands ] , & image [ k * num_samples * num_bands + j * num_bands ] , num_bands ) ; if ( dot_product > max ) { max = dot_product ; local_results [ idx * 6 + 0] = k ; // maxX local_results [ idx * 6 + 1] = j ; // maxY local_results [ idx * 6 + 2] = dot_product ; } if ( dot_product < min ) { min = dot_product ; local_results [ idx * 6 + 3] = k ; // minX local_results [ idx * 6 + 4] = j ; // minY local_results [ idx * 6 + 5] = dot_product ; } } } idx += num_skewers } Código 6.1: Código del kernel del algoritmo PPI para la implementación sin optimizaciones Este código o kernel es ejecutado por cada hilo de la GPU. La configuración de lanzamiento es de 30 bloques con 512 hilos de ejecución en cada uno con lo que se consiguen 30 × 512 = 15360 hilos de ejecución, uno para cada skewer. La tabla 6.4 muestra la ocupación de configuración de la GPU para una tarjeta versión 1.3, como las Nvidia C1060 del entorno de Test del clúster de CETA-CIEMAT. En este caso la ocupación de la tarjeta es del 100 %. Por lo general, una mayor ocupación conlleva una mejora del rendimiento del algoritmo, aunque no en todos los casos. Con esta misma configuración de bloques e hilos se obtiene en una tarjeta 2.0, como las Nvidia C2050 y C2070 del entorno de Producción del clúster de CETA-CIEMAT, una ocupación del 67 % (ver tabla 6.5). Este dato para esta versión no es relevante, dado que en esta ocasión no está orientada a la obtención del mayor rendimiento. Dato que mejorará en las sucesivas versiones orientadas a la reducción del tiempo de ejecución del algoritmo. Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 512 11 56 100 % Tabla 6.4: Datos de ocupación de la GPU para la implementación MPI-CUDA sin optimizaciones del algoritmo PPI para el entorno de Test del clúster de CETA-CIEMAT Una vez que se ha descrito la implementación desarrollada, se pasará a realizar una comparativa entre los resultados obtenidos por la versión del algoritmo PPI para clúster de GPUs y los resultados obtenidos por el software comercial ENVI. Dicha comparativa surge con el ánimo de comprobar si 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 45 512 23 0 67 % Tabla 6.5: Datos de ocupación de la GPU para la implementación MPI-CUDA sin optimizaciones del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT esta versión del algoritmo PPI para clúster de GPUs se puede aplicar a la práctica diaria del análisis de imágenes hiperespectrales. Como se comentó en el capı́tulo 4, el algoritmo PPI pertenece al conjunto de métodos interactivos, dado que se hace necesaria una etapa interactiva final en la que el usuario debe seleccionar manualmente aquellos pı́xeles que quiere utilizar como firmas espectrales puras o endmembers y, consecuentemente, el conocimiento a priori que éste tenga de la imagen resultará muy útil. Para comprobar la efectividad del algoritmo se utilizará la imagen capturada por el sensor AVIRIS sobre la región minera de Cuprite, en el estado de Nevada (Estados Unidos). Esta imagen se caracteriza porque en ella se manifiestan singularidades que permiten discriminar entre una amplia gama de minerales de tipo calizo. Una forma adecuada y bastante ilustrativa de comprobar la efectividad de una versión de PPI es comprobar visualmente los resultados obtenidos de la ejecución de dicho algoritmo con los resultados que ofrece ENVI para un caso de aplicación real como la detección de minerales. Respecto a la implementación de PPI que integra ENVI, cabe destacar que ésta aplica un proceso denominado Fast Pixel Purity Index [11] que permite incrementar el número de pı́xeles que se seleccionan en cada proyección como candidatos a firma espectral pura. Esta singularidad la hace diferente de la especificación establecida por la versión inicial del algoritmo [7] que indica que solamente se seleccionan aquellos pı́xeles cuya proyección es máxima o mı́nima sobre el vector aleatorio. A continuación se muestran los resultados del algoritmo PPI obtenidos tras ejecutar las implementaciones para clúster de GPUs y ENVI (ver figura 6.4). (a) Resultados ENVI (b) Resultados clúster de GPUs Figura 6.4: Resultados visuales obtenidos de la ejecución del algoritmo PPI para las versiones de ENVI y clúster de GPUs sobre la imagen Cuprite A la vista de estos resultados, cabe destacar que ambas imágenes son muy similares, presentando regiones de candidatos a firmas espectrales puras en coordenadas espaciales casi coincidentes, lo cual permite afirmar que ambas versiones son comparables y que es posible encontrar el mismo conjunto de firmas espectrales puras. Dado que los resultados son prácticamente los mismos, podemos afirmar que la versión del algoritmo PPI para clúster de GPUs es válida para su uso en la práctica diaria del análisis de imágenes hiperespectrales. Por otro lado y a modo de ejemplo, La figura 6.5 presenta algunos de los minerales que se pueden detectar en la imagen resultado de la ejecución del algoritmo 46 CAPÍTULO 6. RESULTADOS EXPERIMENTALES para la imagen de Cuprite. El cuadrado verde señala la localización de la Kaolinita, la situación de la Alunita se destaca en azul y en naranja se ubica la Calcita, presentándose en la figura 6.6 la firma espectral de dichos minerales. (a) Resultados ENVI (b) Resultados clúster de GPUs Figura 6.5: Ejemplo de algunos de los minerales detectados en la imagen resultado de la ejecución de PPI sobre la imagen Cuprite (a) Kaolinita (verde) (b) Alunita (azul) (c) Calcita (naranja) Figura 6.6: Firmas espectrales de los minerales detectados en la imagen resultado de la ejecución de PPI para clúster de GPUs sobre la imagen Cuprite Para continuar con el estudio de la efectividad del algoritmo PPI presentado en esta sección, se presentan además los resultados obtenidos de la ejecución del algoritmo para la imagen del Lago St. Clair. Al contrario que en el caso anterior, esta imagen ha sido seleccionada aleatoriamente de entre las publicadas por el Jet Propulsion Laboratory de NASA principalmente por su tamaño y por la presencia de agua, vegetación y edificaciones entre otros elementos, y no ha sido tan estudiada en la literatura reciente como la imagen de Cuprite. La figura 6.7 muestra los resultados obtenidos por la ejecución del algoritmo PPI en sus versiones para clúster de GPUs y ENVI. En esta ocasión, se puede comprobar visualizando los resultados que ENVI ha seleccionado un mayor número de pı́xeles como candidatos a endmember que la versión para clúster de GPUs, debido principalmente a la implementación Fast PPI, que selecciona un número mayor de pı́xeles en cada iteración. Aun ası́, ambas imágenes son muy similares, seleccionando pı́xeles en coordenadas casi coincidentes, tal y como ocurrı́a con los resultados de la imagen anterior. En dicha figura se han resaltado algunas zonas con presencia de agua (en azul), bancos de arena (en naranja) y vegetación (en verde). Cabe comentar que no nos es posible presentar con mayor detalle estos resultados por el desconocimiento de la imagen al no contar con datos de verdad-terreno. Con respecto a las pruebas con la imagen de mayor tamaño, correspondiente a Yellowstone, no se ha podido realizar una ejecución de PPI con el software ENVI porque dicha imagen provoca un error de ejecución y, por consiguiente, no se puede realizar la comparación visual de los resultados al no contar con una de las imágenes necesarias para dicho fin. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS (a) Resultados ENVI 47 (b) Resultados clúster de GPUs Figura 6.7: Resultados visuales obtenidos de la ejecución del algoritmo PPI para las versiones de ENVI y clúster de GPUs sobre la imagen Lago St. Clair Para finalizar con el análisis de la efectividad del algoritmo, y a modo de conclusión, se puede afirmar que la implementación del algoritmo PPI para clúster de GPUs es una buena alternativa a las implementaciones existentes como la incluida en ENVI, fundamentado tras haberse comprobado que puede ser utilizada en casos reales de análisis de imágenes hiperespectrales. Dejando a un lado la efectividad del algoritmo, se pasa al estudio del rendimiento que ofrece. A continuación se presentan las mediciones de tiempo y las gráficas asociadas de los resultados obtenidos de la ejecución del algoritmo en el entorno de Producción de CETA-CIEMAT. En cada gráfica se representa la evolución del tiempo de ejecución del algoritmo en cada nodo (CT) y el tiempo total del algoritmo (TT) y para ejecuciones de 1 a 16 nodos para con las tres imágenes de entrada: Cuprite, Lago St. Clair y Yellowstone. La tabla 6.6 muestra el promedio de los resultados medidos en segundos para cada configuración de número de nodos de cálculo y la imagen de entrada Cuprite. La gráfica 6.8a representa la evolución del tiempo de ejecución del algoritmo en cada nodo (CT) y el tiempo total del algoritmo (TT) para cada configuración. IRT 0,007466 0,005778 0,008936 0,005108 0,005872 0,005722 0,005783 0,001496 0,003714 0,006049 0,003841 0,005920 0,004940 0,005839 0,005716 SKT 0,098670 0,096527 0,098125 0,101290 0,096295 0,095356 0,096302 0,096338 0,096112 0,096368 0,095777 0,096344 0,096601 0,096368 0,096300 BT 0,000021 0,008008 0,010607 0,013208 0,014514 0,014878 0,015449 0,016772 0,017010 0,018203 0,018029 0,018605 0,018767 0,019017 0,019635 ScT 0,065842 0,067687 0,074246 0,076521 0,075650 0,074696 0,076539 0,079188 0,078945 0,081345 0,079236 0,079365 0,079926 0,079987 0,081071 CT 343,879915 174,709909 118,781294 90,742096 73,657719 63,115902 54,367685 48,164924 43,778489 39,611804 36,565600 34,351460 31,930619 29,959470 29,239362 GT 343,880037 174,843165 118,781774 90,742674 73,952397 63,228940 54,532515 48,687444 43,936143 40,253398 37,340465 34,831334 32,554513 30,703032 29,466519 PPT 0,000086 0,000153 0,000198 0,000259 0,000271 0,000336 0,000390 0,000479 0,000549 0,000612 0,000689 0,000770 0,000849 0,000929 0,001012 TT 344,059276 175,028680 118,982667 90,946680 74,153451 63,429148 54,736143 48,886653 44,140178 40,465447 37,545533 35,042957 32,765289 30,916052 29,680364 Tabla 6.6: Resultados de ejecución (en sg.) de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) 48 CAPÍTULO 6. RESULTADOS EXPERIMENTALES (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.8: Gráficas de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) Como se puede observar, con esta primera versión se ha conseguido procesar la imagen Cuprite en un tiempo menor a 30 segundos utilizando 15 nodos de cálculo. Dada la evolución de la gráfica, no se prevé que se puedan alcanzar tiempos mucho menores con un mayor número de nodos, siendo debido principalmente al tamaño de la imagen (aprox. 44 MB). Esto se ve claramente en la gráfica de speedup, cuyo crecimiento no aumenta de forma lineal en relación al número de nodos. La tabla 6.7 y las gráficas 6.9a y 6.9b muestran el promedio de los resultados medidos en segundos y las gráficas asociadas para cada configuración de número de nodos de cálculo y la imagen de entrada Lago St. Clair. IRT 0,002542 0,003506 0,001745 0,001518 0,001624 0,002574 0,002549 0,003651 0,004518 0,003418 0,006078 0,005709 0,006008 0,005934 0,003697 SKT 0,122611 0,120154 0,117872 0,117741 0,118098 0,119728 0,114798 0,119554 0,117937 0,114695 0,114959 0,113392 0,114950 0,114741 0,114778 BT 0,000029 0,009649 0,013125 0,015572 0,016780 0,018758 0,018530 0,020695 0,020349 0,020803 0,020788 0,021230 0,022057 0,022108 0,021808 ScT 0,328790 0,352597 0,369838 0,370279 0,375730 0,372674 0,366866 0,373793 0,383687 0,401043 0,400755 0,403661 0,413443 0,386402 0,390773 CT 1682,914922 844,719698 564,085151 424,301137 340,638141 280,996522 248,948807 214,010589 190,348702 175,613142 161,405213 148,692089 136,931735 127,406702 120,701931 GT 1682,915040 845,837891 564,554586 424,301872 340,678577 281,971433 249,328818 214,177324 191,033256 176,158855 161,406591 148,693567 137,035074 128,028677 120,703738 PPT 0,000090 0,000161 0,000199 0,000246 0,000292 0,000346 0,000382 0,000483 0,000567 0,000622 0,000691 0,000770 0,000850 0,000917 0,000997 TT 1683,373763 846,329213 565,062664 424,812375 341,196782 282,492184 249,838687 214,703762 191,569801 176,707793 161,960920 149,250247 137,603646 128,571139 121,246150 Tabla 6.7: Resultados de ejecución (en sg.) de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) A la vista de los resultados vemos como la escalabilidad del algoritmo mejora con el aumento del tamaño de la imagen. Para estas configuraciones (de 1 a 15 nodos), el speedup del algoritmo crece de forma casi lineal, alcánzandose valores de casi el 14x de mejora de la ejecución de 15 nodos frente a la de 1 nodo. Esto se debe principalmente a que el tiempo de las transferencias de los datos de entrada y demás comunicaciones pierden peso frente al tiempo de ejecución del algoritmo. Por último, se presentan los resultados obtenidos para la ejecución del algoritmo que nos ocupa con la imagen de mayor tamaño: Yellowstone. La tabla 6.8 muestra el promedio de los tiempos medidos para cada fase del algoritmo y las gráficas 6.10a y 6.10b representan la evolución del tiempo de ejecución (CT), el tiempo total (TT) y el speedup, para la misma configuración de ejecución que los casos anteriores. A primera vista de los resultados se observa que el tiempo de ejecución del algoritmo para un nodo es muy pequeño en relación al resto de ejecuciones. Esto es debido a que la GPU no ha permitido la reserva de memoria para un tamaño de imagen tan grande, abortando la ejecución del algoritmo. Esto a su vez se debe a que, aunque la GPU disponga de memoria suficiente para alojar la imagen 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS (a) Gráfica de CT y TT (en sg.) 49 (b) Gráfica de speedup Figura 6.9: Gráficas de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) IRT 0,002139 0,007377 0,002374 0,003407 0,004616 0,004000 0,004720 0,002914 0,005917 0,001506 0,003692 0,005748 0,003897 0,002603 0,003471 SKT 0,119449 0,116187 0,113286 0,114167 0,120276 0,116124 0,113344 0,113354 0,113305 0,113079 0,113619 0,113122 0,113118 0,113626 0,113367 BT 0,000022 0,009560 0,012856 0,015207 0,017174 0,017986 0,018532 0,018849 0,020438 0,021222 0,021155 0,021318 0,021902 0,021854 0,022719 ScT 4,961460 5,630827 5,410332 6,870268 7,420957 7,439791 7,325346 7,201049 6,988009 7,048699 7,104953 7,128804 7,164687 7,238491 6,877310 CT 5,766046 14575,538538 9723,635413 7296,974268 5845,612390 4881,273283 4176,625554 3648,802645 3252,257260 2934,371958 2668,094073 2447,168522 2257,754746 2092,317722 1954,929733 GT 5,766228 14601,182773 9738,133839 7302,139137 5847,861562 4881,274071 4183,655707 3659,390417 3257,402571 2935,358991 2668,250562 2447,169969 2257,756324 2092,319388 1954,931492 PPT 0,000087 0,000166 0,000194 0,000238 0,000296 0,000361 0,000403 0,000490 0,000548 0,000619 0,000706 0,000760 0,000859 0,000927 0,001004 TT 11,059273 14607,533542 9744,291464 7309,712133 5856,008346 4889,466504 4191,692540 3667,297256 3265,101265 2943,106586 2676,098618 2455,051376 2265,799866 2100,265150 1962,553448 Tabla 6.8: Resultados de ejecución (en sg.) de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.10: Gráficas de la implementación sin optimizaciones del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) en su memoria global, mayor que el tamaño de la imagen, es muy posible que no exista la memoria contigua necesaria para almacenarla (solicitada por la llamada a la función cudaMalloc()). Por este motivo, la gráfica del speedup (ver figura 6.10b) se ha calculado de acuerdo a la ejecución con menor número de nodos (2 nodos), consiguiendo para la ejecución de 15 nodos una mejora aproximada del 7,5x. Extrapolando el resultado, podemos afirmar que el speedup total conseguido frente a la versión de un solo nodo serı́a superior a 14x, muy cercano al speedup lineal (15x). En resumen y a la vista de los resultados obtenidos para cada imagen, el factor de aceleración de esta primera versión del algoritmo va mejorando según aumenta el tamaño de la imagen de entrada. Además, se ha conseguido un speedup lineal para el caso de la imagen de mayor tamaño. Hay que 50 CAPÍTULO 6. RESULTADOS EXPERIMENTALES destacar también que para esta imagen, se han producido errores durante la ejecución del algoritmo para un solo nodo de computación, debido a que no se ha podido copiar la imagen a la memoria dedicada de la GPU. En la siguiente versión se intentará solucionar este problema con el uso de la caracterı́stica denominada Zero Copy de CUDA, que supone un acceso directo a la memoria de la CPU sin necesidad de copiar la imagen a la memoria dedicada de la GPU. 6.2.2. Implementación Zero Copy La primera evolución de la implementación inicial del algoritmo PPI para clúster de GPUs va orientada a corregir el error producido en la GPU cuando se intenta reservar memoria para la imagen de Yellowstone. Esto es debido a que la llamada a la función cudaMalloc() que se utiliza en la implementación inicial devuelve un error ya que no puede reservar un espacio de memoria contiguo tan grande. Para evitar la copia del fragmento de la imagen de la memoria del host a la memoria de la GPU se ha utilizado la caracterı́stica denominada Zero Copy. Con esta técnica, se mapea un área de memoria de la GPU con una zona de memoria del host no paginable, evitando la copia explı́cita de los datos. La memoria del host debe ser no paginable o pinned, es decir, que no puede ser paginada a disco, para acelerar las transferencias al no tener que hacerse comprobaciones referentes a la paginación. Para ello se utiliza la función de CUDA cudaHostAllocMapped(). cudaSetDeviceFlags ( cudaDeviceMapHost ); cudaHostAlloc (( void **) & pm_image_chunk , ( chunk_size * num_samples * num_bands * sizeof ( float ) ) , c u d a H o s t A l l o c W r i t e C o m b i n e d | c u d a H o s t A l l o c M a p p e d ) ; cudaMemcpy ( pm_image_chunk , image_chunk , chunk_size * num_samples * num_bands * sizeof ( float ) , c u d a M e m c p y H o s t T o H o s t ) ; c u d a H o s t G e t D e v i c e P o i n t e r (& d_image_chunk , pm_image_chunk , 0) ; Código 6.2: Cálculo de la selección de skewer para cada kernel El código 6.2 muestra las lı́neas necesarias para realizar este proceso en CUDA. El primer paso es activar en el dispositivo el uso de memoria no paginada mediante la llamada a la función cudaSetDeviceFlags() con el flag cudaDeviceMapHost. Hecho esto, se podrá reservar el espacio de memoria no paginable mediante invocaciones a la función cudaHostAlloc(). En esta implementación se han utilizado los flags cudaHostAllocMapped, que permite “notificar” al entorno de ejecución el uso de una zona de memoria como mapeable entre el host y el dispositivo (en definitiva, que se va a usar como Zero Copy), y cudaHostAllocWriteCombined, que omite el uso de las cachés L1 y L2 de la CPU, permitiendo el acceso directo a la memoria RAM. Posteriormente se copia el contenido de la imagen a la memoria no paginable y por último se crea una referencia en la GPU para tener acceso desde el dispositivo. En cuanto al código a ejecutar en la GPU, éste no cambia respecto a la versión anterior (ver código 6.3). int idx = blockDim . x * blockIdx . x + threadIdx . x ; while ( idx < num_skewers ) { float min = FLT_MAX , max = FLT_MIN , dot_product = 0.0; for ( int k = 0; k < num_lines ; k ++) { for ( int j = 0; j < num_samples ; j ++) { dot_product = dotProduct (& skewers [ idx * num_bands ] , & image [ k * num_samples * num_bands + j * num_bands ] , num_bands ) ; if ( dot_product > max ) { max = dot_product ; local_results [ idx * 6 + 0] = k ; // maxX local_results [ idx * 6 + 1] = j ; // maxY local_results [ idx * 6 + 2] = dot_product ; } if ( dot_product < min ) { min = dot_product ; local_results [ idx * 6 + 3] = k ; // minX local_results [ idx * 6 + 4] = j ; // minY local_results [ idx * 6 + 5] = dot_product ; } } } idx += num_skewers } Código 6.3: Código del kernel del algoritmo PPI para la implementación Zero Copy 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS 51 A continuación, se muestran los resultados de las pruebas realizadas para esta segunda implementación del algoritmo PPI para clúster de GPUs. Las configuraciones y los datos de entrada son los mismos que en la versión inicial. Para estas pruebas se ha conservado la configuración de 30 bloques con 512 hilos de ejecución cada uno, manteniendo la premisa de un hilo de ejecución por skewer. La tabla 6.9 muestra la ocupación de esta segunda versión del algoritmo para las GPUs del entorno de Producción. La ocupación continúa al 67 %. Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 512 23 0 67 % Tabla 6.9: Datos de ocupación de la GPU para la implementación MPI-CUDA Zero Copy del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT La tabla 6.10 muestra el promedio de los resultados medidos en segundos para cada configuración de número de nodos de cálculo y la imagen de entrada Cuprite. Las gráficas 6.11a y 6.11b representan la evolución del tiempo de ejecución del algoritmo en cada nodo (CT) y el tiempo total del algoritmo (TT) ası́ como del speedup para cada configuración. IRT 0,005613 0,005000 0,005083 0,004657 0,004939 0,005775 0,005894 0,003303 0,006029 0,005888 0,004857 0,003085 0,002307 0,002877 0,002971 SKT 0,097593 0,100252 0,096184 0,097622 0,097505 0,096411 0,095794 0,096202 0,096582 0,096546 0,095726 0,096460 0,096680 0,096360 0,096482 BT 0,000002 0,008054 0,010943 0,012967 0,013675 0,014079 0,015303 0,016754 0,016950 0,017506 0,017809 0,018333 0,018274 0,018044 0,019255 ScT 0,066733 0,074171 0,073975 0,075275 0,078830 0,077357 0,078238 0,079439 0,078743 0,079657 0,079033 0,078841 0,080682 0,080814 0,080902 CT 340,759616 174,724443 118,527903 91,378705 73,442751 63,846850 54,989670 49,081655 44,126817 40,069551 37,447870 35,657757 32,972948 30,673825 29,773322 GT 340,759732 174,985334 120,123084 91,577520 74,747782 63,973611 56,086529 49,585962 45,421145 40,906307 38,291077 36,056652 33,468409 31,587739 30,215363 PPT 0,000084 0,000158 0,000193 0,000262 0,000283 0,000383 0,000437 0,000502 0,000633 0,000620 0,000803 0,000852 0,001101 0,000922 0,001210 TT 340,936536 175,179630 120,316493 91,775458 74,951083 64,176326 56,290965 49,789107 45,629756 41,116369 38,498288 36,262089 33,674957 31,795756 30,425026 Tabla 6.10: Resultados de ejecución (en sg.) de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.11: Gráficas de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) A la vista de los resultados de esta etapa para la primera imagen, se puede afirmar que no se ha obtenido una mejora del tiempo de ejecución del algoritmo para la imagen de Cuprite, pero tampoco el 52 CAPÍTULO 6. RESULTADOS EXPERIMENTALES uso de acceso directo a memoria host ha empeorado los resultados del algoritmo. Esto probablemente viene dado por los mecanismos de cacheo de datos de la tarjeta GPU implementados en la arquitectura Fermi, los cuales tratan de reducir al máximo las transferencias de memoria. Por otro lado, los datos de la gráfica 6.11b nos muestran que para esta segunda implementación la escalabilidad empeora ligeramente, obteniéndose un factor de aceleración inferior a 12x de la ejecución para 15 nodos frente a la de 1 solo nodo. La tabla 6.11 y las gráficas 6.12a y 6.12b muestran el promedio de los resultados medidos en segundos y las gráficas asociadas para la imagen de entrada Lago St. Clair. IRT 0,002610 0,004885 0,005645 0,006044 0,005730 0,003090 0,004931 0,004837 0,005452 0,002642 0,003322 0,006156 0,005950 0,004031 0,006022 SKT 0,119456 0,117834 0,117634 0,114526 0,114706 0,114812 0,114177 0,114627 0,114313 0,114446 0,115052 0,114588 0,114777 0,113935 0,114578 BT 0,000002 0,009521 0,012812 0,014966 0,016159 0,016930 0,017445 0,018705 0,019816 0,020343 0,020101 0,021278 0,021308 0,041220 0,021642 ScT 0,310544 0,367247 0,378820 0,382612 0,386493 0,385774 0,384678 0,384178 0,397159 0,397886 0,401576 0,402065 0,411273 0,386635 0,390141 CT 1681,069079 843,238162 564,876730 426,538869 342,878733 285,884617 248,252611 217,737151 192,627282 175,812533 162,411108 148,671750 136,169852 127,812292 120,788521 GT 1681,069193 844,528266 566,889622 428,034216 344,323565 287,720566 248,535986 218,620947 194,100704 177,143451 162,412449 148,807031 137,803295 129,077415 121,773408 PPT 0,000087 0,000156 0,000206 0,000251 0,000326 0,000378 0,000542 0,000527 0,000549 0,000755 0,001114 0,001204 0,000857 0,001230 0,001319 TT 1681,506842 845,035675 567,413802 428,562247 344,856770 288,249343 249,067529 219,153517 194,648119 177,687649 162,962069 149,364141 138,369482 129,633940 122,319136 Tabla 6.11: Resultados de ejecución (en sg.) de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.12: Gráficas de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) De manera similar a lo ocurrido con la implementación inicial para esta imagen de entrada, el factor de aceleración conseguido para la imagen del Lago St. Clair ha mejorado, alcanzando un valor muy próximo a 14x. En este caso la diferencia con respecto a los speedups alcanzados para la primera versión es muy pequeña. En cuanto al tiempo de ejecución, el mejor resultado (versión de 15 nodos) ha empeorado en más de 1 segundo respecto a la primera implementación, posiblemente debido al mayor movimiento de datos entre el host y la GPU. Por último, se presentan los resultados medidos para el procesamiento de la imagen de Yellowstone. Dados los resultados para las dos imágenes de menor tamaño, se puede determinar a priori que esta versión mejorará el cuanto a speedup, pero puede que empeore en cuanto a tiempo de ejecución. Los datos se presentan en la tabla 6.12 y las gráficas 6.13a y 6.13b. Tal y como se comentó anteriormente, los resultados obtenidos del procesamiento de la imagen Yellowstone han confirmado la mejora del factor de aceleración respecto a las ejecuciones con las imágenes de menor tamaño. Como ocurrió con la versión inicial, se ha conseguido un speedup lineal. En cuanto al tiempo de ejecución, en contra con lo afirmado anteriormente, ha disminuido 30 segundos 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS IRT 0,001432 0,005772 0,006223 0,005669 0,011909 0,006029 0,005811 0,001720 0,005850 0,004941 0,005888 0,005288 0,002029 0,005939 0,004008 SKT 0,114679 0,121186 0,120642 0,114923 0,114950 0,120580 0,114981 0,114288 0,114937 0,114470 0,114480 0,114675 0,114467 0,114968 0,114481 BT 0,000002 0,009676 0,012811 0,015088 0,016499 0,016734 0,018201 0,019526 0,020080 0,020789 0,019682 0,021040 0,021658 0,023319 0,022307 ScT 5,230289 5,677227 5,666692 6,792180 7,373056 7,496450 7,198692 7,258234 7,052949 7,086271 7,161734 7,205575 7,212340 7,294426 6,947605 CT 28710,449690 14350,617940 9626,155269 7207,478735 5759,512698 4820,486999 4119,450682 3616,152097 3207,720680 2888,539941 2625,830895 2405,140376 2221,554608 2063,074039 1927,449961 53 GT 28710,474150 14418,722470 9626,155737 7218,682973 5775,036039 4820,487800 4124,393175 3616,153587 3210,328402 2889,458959 2629,822069 2411,145821 2225,625693 2067,114146 1930,864148 PPT 0,000102 0,000162 0,000239 0,000237 0,000281 0,000486 0,000374 0,000746 0,000548 0,000596 0,000819 0,000884 0,000840 0,000918 0,000996 TT 28715,856490 14424,579010 9632,005186 7225,660245 5782,595961 4828,158814 4131,774424 3623,592535 3217,566989 2896,729635 2637,172386 2418,564595 2233,016808 2074,597593 1937,997324 Tabla 6.12: Resultados de ejecución (en sg.) de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.13: Gráficas de la implementación Zero Copy del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) respecto a la versión inicial, procesándose la imagen en 32 minutos aproximadamente, lo cual no resulta muy coherente. Este hecho puede ser debido a diversas causas, desde una gestión diferente de los accesos respecto a la memoria global, hasta el que el movimiento de datos entre el host y la GPU y el procesamiento de la imagen se coordinan mejor que en la versión anterior. Debido a que no se ha experimentado una mejora sustancial en el tiempo de ejecución del algoritmo, no se investigará dicho suceso en profundidad para centrar el esfuerzo en otro tipo de mejoras. Cabe destacar que con esta implementación ya no aparece el problema del error de reserva de memoria que impedı́a la ejecución del algoritmo para un solo nodo. Aún ası́, por cuestiones de rendimiento, el uso de acceso directo a la memoria del host no es recomendable para tarjetas GPU con memoria dedicada, recomendándose solo para tarjetas GPU con memoria compartida con la CPU o en los casos en los que los datos de entrada no puedan almacenarse en la memoria de la GPU y se realicen pocos accesos a memoria (read once / write once). En el siguiente apartado se desestima el uso de Zero Copy memory, dado que el problema solamente surge para ejecuciones en un nodo de cómputo, y se implementa el uso de los registros de la GPU para mejorar los tiempos de procesamiento. 6.2.3. Implementación con uso de memoria local Tras comprobar que el uso de memoria Zero Copy soluciona el problema de almacenamiento de datos de gran tamaño en la GPU, se procede a evolucionar la versión inicial con vistas a disminuir el tiempo de ejecución del algoritmo PPI. En la programación GPU, una de las principales razones que afectan al rendimiento de un algoritmo es el patrón de acceso a memoria. Las GPUs proporcionan mecanismos para disminuir los accesos a bloques de memoria contiguos realizados por los threads de 54 CAPÍTULO 6. RESULTADOS EXPERIMENTALES un mismo bloque [2]. Ası́ mismo, la arquitectura Fermi implementa una jerarquı́a de caché de dos niveles, la cual se presenta en el apartado 3.2.1 del capı́tulo 3, orientada a reducir el tiempo de acceso a la memoria DRAM del dispositivo. En la primera versión, presentada en el apartado 6.2.1 del presente capı́tulo, los accesos a memoria global para leer los pı́xeles de la imagen de entrada son a las mismas posiciones de memoria, dado que todos los hilos leen la misma imagen. Eso provoca que se produzcan aciertos de caché y se reduzca el tiempo de acceso a estos datos. Por el contrario, cada hilo debe acceder a una zona de memoria diferente donde se encuentra el skewer que necesita para calcular las proyecciones con cada pı́xel de la imagen. En este caso, como no hay coincidencias de lectura de datos, se producen fallos de caché y como consecuencia el rendimiento del algoritmo empeora. Este comportamiento no deseado será el que intentará corregir la versión del algoritmo basada en memoria local o local memory. La memoria local de la GPU es un espacio privado de memoria que es único para cada hilo de la GPU. Su almacenamiento fı́sico real se produce en la memoria global del dispositivo y consecuentemente tiene los mismos tiempos de acceso que ésta, pero la GPU la gestiona de manera diferente. Primeramente, el alineamiento de los datos almacenados en este tipo de memoria se realiza para favorecer las lecturas en bloque (coalescentes) para hilos contiguos de la CPU. Además, el dispositivo prioriza el almacenamiento de los datos de memoria local a la caché de primer nivel L1 para reducir el número de accesos a la memoria global. La idea que mueve esta versión del algoritmo paralelo PPI es la de almacenar el skewer con el que trabaja un hilo en la memoria privada del mismo, de tal forma que cambie el patrón de acceso a los valores del vector aleatorio y se consiga una mejora del rendimiento. La primera intención de esta versión era el almacenamiento del vector de skewers en los registros de la GPU, los cuales tienen un coste de acceso despreciable, pero el tamaño del vector aleatorio (224 × 4 bytes = 896 bytes) no hacı́a viable esta idea dado que el número de registros asignados a un hilo de la GPU está limitado a 63 como máximo (debido a una restricción del número de bits de direccionamiento [4]) y el número de registros requeridos por el skewer es de 896 bytes/4 bytes por registro = 224. Cabe destacar que es muy importante hacer un uso de los registros de la GPU limitado debido principalmente a que si se supera la cantidad máxima por hilo de ejecución afectará al porcentaje de ocupación de la GPU y muy probablemente al rendimiento. Las GPUs del entorno de Producción del clúster de CETA-CIEMAT tienen un total de 32.768 registros y permiten un total de 1.536 hilos por multiprocesador. Este hecho limita a un máximo de 32768/1536 = 21, 3 registros por hilo para alcanzar la ocupación máxima. El código 6.4 muestra la declaración de un array en la memoria local de la GPU, de tamaño máximo 224, suficiente para alojar el vector aleatorio. Como se puede observar, no es necesario el uso de ningún modificador ni directiva para la declaración de una variable local. float skewer [224]; // Copy a skewer a local memory for ( short k = 0; k < num_bands ; k ++) { skewer [ k ] = skewers [ idx * num_bands + k ]; } Código 6.4: Declaración de un array en memoria local de la GPU (registros) para el almacenamento del skewer con el que trabajará el hilo de ejecución La tabla 6.13 muestra los datos de ocupación de la GPU de esta versión del algoritmo. Como se puede observar, se hace uso de 22 registros por hilo, provocando que la ocupación de la GPU no esté al 100 %. En esta ocasión se ha modificado la configuración de ejecución a 60 bloques de 256 hilos por bloque, lo que supone un aumento de la ocupación al 83 %. Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 256 22 0 83 % Tabla 6.13: Datos de ocupación de la GPU para la implementación MPI-CUDA local memory del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT En cuanto al cuerpo del kernel (ver Código 6.5), el único cambio significativo está relacionado con el uso del array local skewer para el almacenamento del vector aleatorio que se utilizará durante el cálculo de la proyección (producto escalar) de cada pı́xel de la imagen sobre dicho vector. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS 55 int idx = blockDim . x * blockIdx . x + threadIdx . x ; while ( idx < num_skewers ) { float skewer [224]; // Copy a skewer a local memory for ( short k = 0; k < num_bands ; k ++) { skewer [ k ] = skewers [ idx * num_bands + k ]; } float min = FLT_MAX ; float max = FLT_MIN ; float dot_product = 0.0; for ( int k = 0; k < num_lines ; k ++) { for ( int j = 0; j < num_samples ; j ++) { dot_product = dotProduct ( skewer , & image [ k * num_samples * num_bands + j * num_bands ] , num_bands ) ; if ( dot_product > max ) { max = dot_product ; local_results [ idx * 6 + 0] = k ; // maxX local_results [ idx * 6 + 1] = j ; // maxY local_results [ idx * 6 + 2] = dot_product ; } if ( dot_product < min ) { min = dot_product ; local_results [ idx * 6 + 3] = k ; // minX local_results [ idx * 6 + 4] = j ; // minY local_results [ idx * 6 + 5] = dot_product ; } } } idx += num_skewers ; } Código 6.5: Código del kernel del algoritmo PPI para la implementación local memory A continuación se muestran los resultados obtenidos de la ejecución de esta versión de PPI para las tres imágenes. La tabla 6.14 muestra el promedio de los resultados medidos en segundos ejecuciones de 1 a 15 nodos y la imagen de entrada Cuprite. Las gráficas 6.14a y 6.14b representan la evolución del tiempo de ejecución del algoritmo en cada nodo (CT) y el tiempo total del algoritmo (TT) ası́ como del speedup para cada configuración. IRT 0,005101 0,004546 0,005061 0,004993 0,004838 0,004909 0,005328 0,004650 0,005125 0,004728 0,004370 0,004978 0,004749 0,004516 0,005100 SKT 0,098678 0,099765 0,098564 0,098323 0,096915 0,096133 0,096188 0,095941 0,096407 0,096248 0,095983 0,095870 0,096411 0,095691 0,096448 BT 0,000022 0,009976 0,010836 0,012567 0,013854 0,014897 0,015393 0,016424 0,017327 0,018085 0,023903 0,018486 0,018559 0,018518 0,019882 ScT 0,061240 0,066934 0,069347 0,072766 0,075090 0,073440 0,074382 0,075370 0,076165 0,077415 0,077184 0,077182 0,077846 0,078339 0,078548 CT 23,036714 14,448204 11,438934 10,264137 9,381146 8,675665 8,130888 8,131217 7,821537 7,710534 7,582804 7,554543 7,317478 7,173489 6,920080 GT 23,036820 14,639335 11,933519 10,714850 9,859819 9,237495 8,891306 8,631233 8,439901 8,244788 8,120437 7,951359 7,927773 7,830721 7,729951 PPT 0,000086 0,000155 0,000192 0,000237 0,000280 0,000325 0,000384 0,000453 0,000534 0,000617 0,000687 0,000765 0,000832 0,000909 0,000976 TT 23,208729 14,826948 12,124826 10,911214 10,058303 9,435137 9,091374 8,832101 8,643854 8,450503 8,330652 8,157887 8,135422 8,038347 7,940828 Tabla 6.14: Resultados de ejecución (en sg.) de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) A la vista de los resultados obtenidos se puede apreciar que se ha reducido de forma notable el tiempo de ejecución del algoritmo, pasando de los 30 segundos de la versión inicial a los apenas 8 56 CAPÍTULO 6. RESULTADOS EXPERIMENTALES (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.14: Gráficas de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) segundos que tarda la ejecución de la versión descrita en este apartado para 15 nodos. Esta reducción notable del tiempo de ejecución afecta al speedup conseguido, que apenas alcanza el 2,5x para el tiempo total de ejecución, principalmente provocado por el coste de las transferencias de datos entre los nodos de cómputo en primer lugar y en segundo lugar por el de las transferencias entre el host y el dispositivo. Se ha calculado el tiempo de ejecución del kernel en la GPU y es de 1, 29 sg., frente a los 22, 19 sg. calculados para la versión inicial del algoritmo que no implementaba optimizaciones. Tras presentar los resultados obtenidos para la imagen de Cuprite, se muestran a continuación aquellos obtenidos para la imagen del Lago St. Clair. La tabla 6.15 muestra el promedio de los tiempos de ejecución para configuraciones de 1 a 15 nodos y posteriormente las figuras 6.15a y 6.15b ilustran la evolución del tiempo de cómputo (CT) y el tiempo total (TT) según evoluciona el número de nodos de cómputo, ası́ como la evolución del speedup para dichas configuraciones. Dados los resultados obtenidos para la imagen más pequeña, cabrı́a esperar también una notable mejora de rendimiento para la imagen del Lago St. Clair. IRT 0,003719 0,004849 0,003828 0,002143 0,004362 0,004364 0,004412 0,002041 0,005047 0,004423 0,004682 0,005437 0,003935 0,004707 0,002842 SKT 0,121321 0,120804 0,120770 0,120860 0,119432 0,118761 0,118925 0,118898 0,114452 0,114559 0,114609 0,114734 0,114598 0,114100 0,114798 BT 0,000026 0,009528 0,012839 0,015241 0,016289 0,017503 0,018343 0,019459 0,020021 0,021114 0,020731 0,021749 0,021294 0,022275 0,022932 ScT 0,309108 0,375402 0,365294 0,380260 0,378752 0,394136 0,382319 0,376856 0,396691 0,398375 0,400935 0,403094 0,409218 0,383360 0,388511 CT 93,201709 49,118143 34,574901 27,892955 23,358827 20,223921 18,328444 16,907292 15,478415 14,766224 13,964797 13,280844 12,761645 12,020751 11,829046 GT 93,201821 49,153271 34,699710 27,990033 23,783166 20,871107 18,944631 17,348269 15,907097 14,993860 14,232090 13,525592 13,155483 12,671629 12,243934 PPT 0,000090 0,000161 0,000206 0,000246 0,000293 0,000347 0,000399 0,000458 0,000542 0,000625 0,000688 0,000759 0,000849 0,000925 0,000999 TT 93,642087 49,671612 35,210487 28,514849 24,310824 21,414485 19,477920 17,872652 16,453214 15,542197 14,783668 14,082083 13,714948 13,206913 12,782979 Tabla 6.15: Resultados de ejecución (en sg.) de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) Tal y como se esperaba, el tiempo de ejecución de esta versión del algoritmo también se ha reducido de forma notable respecto a las versiones anteriores, bajando de los 2 minutos que tardaba la versión inicial para 15 nodos a los aproximadamente 13 segundos de ésta para la imagen de tamaño medio. A la vista de las gráficas de rendimiento, el speedup del algoritmo mejora frente al obtenido para la imagen de Cuprite, alcanzando una mejora del 7x debida principalmente a que las comunicaciones entre nodos y la transferencia entre host y GPU pierden peso frente al tiempo empleado en la ejecución del algoritmo. Si se sigue esta tendencia, la ejecución para la imagen de entrada de Yellowstone será la que alcance un mayor factor de aceleración y también verá reducido significativamente el tiempo de ejecución. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS (a) Gráfica de CT y TT (en sg.) 57 (b) Gráfica de speedup Figura 6.15: Gráficas de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) Siguiendo el esquema de presentación de resultados utilizado a lo largo de esta memoria, la tabla 6.16 presenta el promedio de los tiempos de ejecución medidos para cada uno de los parámetros descritos en el apartado 6.1.2 del presente capı́tulo y las figuras 6.16a y 6.16b muestran la evolución de los tiempos de ejecución y del speedup según aumenta el número de nodos de cómputo utilizados. IRT 0,004491 0,004753 0,004310 0,004867 0,004299 0,004465 0,004419 0,004053 0,004449 0,004926 0,005443 0,003794 0,004394 0,004390 0,002909 SKT 0,120952 0,120826 0,120770 0,118830 0,120946 0,118683 0,114942 0,114759 0,114756 0,114766 0,114614 0,114438 0,114184 0,114633 0,114631 BT 0,000026 0,009431 0,012805 0,015365 0,016337 0,017854 0,018821 0,019254 0,019887 0,020198 0,020656 0,022006 0,021748 0,022366 0,021991 ScT 5,047203 5,439158 5,225850 6,965751 7,270667 7,314724 7,205072 7,065163 7,024875 7,093697 7,156510 7,201065 7,237853 7,281797 6,925390 CT 5,701569 756,793811 503,848671 380,497205 305,191945 255,139061 220,607593 193,779202 173,153925 156,300174 142,844738 130,888048 121,699176 113,330393 105,938751 GT 5,701641 756,794192 504,717033 380,497804 305,192636 255,180258 220,608767 193,780601 173,155453 156,301425 142,846084 130,889500 121,700737 113,332079 105,940529 PPT 0,000090 0,000163 0,000205 0,000242 0,000294 0,000346 0,000392 0,000458 0,000542 0,000625 0,000693 0,000763 0,000844 0,000920 0,000987 TT 10,909211 762,411248 510,126280 387,639080 312,648455 262,676779 227,995265 201,028339 180,362056 163,578063 150,187122 138,274753 129,123323 120,801127 113,048970 Tabla 6.16: Resultados de ejecución (en sg.) de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.16: Gráficas de la implementación local memory del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) Al igual que ocurrı́a con la imagen de Lago St. Clair, esta versión del algoritmo PPI ha reducido también el tiempo de ejecución respecto a las versiones anteriores, siendo el menor tiempo de ejecución el alcanzado por la configuración de 15 nodos. Esto significa que se ha conseguido procesar una imagen de 4 GB en menos de 2 minutos de tiempo. Además, se ha conseguido un speedup del 7x respecto 58 CAPÍTULO 6. RESULTADOS EXPERIMENTALES a la versión que utiliza 2 nodos, puesto que, como ocurrı́a en la versión inicial, el procesamiento de ésta imagen en un solo nodo de cómputo provoca errores al no poderse almacenar dicha imagen en la memoria del dispositivo. Extrapolando los datos podemos afirmar que se consiguirı́a un speedup en torno al 12,5x respecto a la versión de un solo nodo. 6.2.4. Implementación con optimizaciones de compilación En la implementación descrita en el apartado anterior se introdujo un cambio en la gestión de los skewers que provocó una reducción notable del tiempo de ejecución del algoritmo PPI, destacando especialmente el procesamiento de la imagen Yellowstone que alcanzó una reducción de más del 80 % en el tiempo total de ejecución. Aun ası́, la ocupación de la GPU no alcanzaba el 100 %, por lo que en esta cuarta versión se intentará alcanzar una ocupación total del dispositivo de cara a reducir aún más el tiempo de ejecución. Para ello, partiendo de la información proporcionada por las gráficas de la herramienta CUDA Occupancy Calculator se identifica que mediante una reducción a 20 del número de los registros por thread de la GPU, el cual asciende a 22 en la versión local memory, se puede conseguir una ocupación del 100 % de la GPU. Para conseguir este fin, se utilizará el parámetro que proporciona el compilador de CUDA --maxrregcount, que limita el número máximo de registros por thread que utilizará la GPU. Esta opción funciona de la siguiente manera: si el número de registros introducido como lı́mite es múltiplo de 4 se usará ese valor y en caso contrario se establecerá como lı́mite el múltiplo de 4 mayor más próximo. Aplicando esta premisa a nuestro problema, se establece el número de registros a utilizar a 16. No se ha seleccionado un valor mayor que sea múltiplo de 4 a efectos de evaluar cómo varı́a el rendimiento con una diferencia más amplia. Además, se vuelve a recuperar la configuración de 30 bloques con 512 hilos cada uno dado que esta configuración también produce un 100 % de ocupación. La tabla 6.17 muestra la configuración para esta versión. Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 512 16 0 100 % Tabla 6.17: Datos de ocupación de la GPU para la implementación MPI-CUDA con optimizaciones de compilación del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT En cuanto al código del kernel que se ejecutará en la GPU, no varı́a respecto a la versión local memory descrita en la versión anterior (ver código 6.5 del apartado 6.2.3). Adicionalmente a la reducción del número de registros y dado que la implementación del kernel no utiliza memoria compartida, se procede al aumento de la capacidad de la memoria caché L1 para propiciar un aumento de aciertos de caché que permita reducir el número de accesos a memoria global de la GPU y, por lo tanto, mejorar el rendimiento del algoritmo. Para ello se introduce como modificación el código 6.6. Esta configuración, aplicada antes de la invocación del kernel, solicita a la GPU el aumento de tamaño de la caché L1 a 48KB, en lugar de los 16KB asignados por defecto, en detrimento del tamaño asignado a la memoria compartida de bloque que se ajusta a 16KB. La función cudaFuncSetCacheConfig() permite configurar programáticamente para un kernel dado la preferencia de uso de memoria compartida o caché L1. El flag cudaFuncCachePreferL1 indica la preferencia por la segunda opción. c u d a F u n c S e t C a c h e C o n f i g ( " ppi " , c u d a F u n c C a c h e P r e f e r L 1 ) ; Código 6.6: Código que permite el aumento del tamaño de la caché L1 de la GPU La tabla 6.18 presenta los resultados obtenidos con esta versión del algoritmo para la imagen Cuprite. Como ocurre en la versión anterior basada en local memory, no se manifiesta una reducción del tiempo total de ejecución del algoritmo, debido principalmente a la penalización que introducen las transferencias de datos entre la CPU y la GPU y la comunicación entre los nodos de cómputo. En cuanto al factor de aceleración, el algoritmo evoluciona de manera similar al anterior, consiguiendo una mejora ligeramente superior (>3x), pero continuando con la pobre escalabilidad del algoritmo para imágenes de pequeño tamaño. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS IRT 0,004272 0,004055 0,003893 0,003262 0,003219 0,002674 0,002918 0,002817 0,002532 0,002969 0,002993 0,003364 0,003365 0,003656 0,004502 SKT 0,100712 0,100920 0,100094 0,097691 0,096945 0,098698 0,098385 0,096385 0,096208 0,096315 0,096402 0,096283 0,096184 0,096338 0,096468 BT 0,000002 0,008010 0,010844 0,012914 0,014038 0,015284 0,015604 0,016883 0,017303 0,017963 0,018132 0,018521 0,019343 0,018548 0,019332 ScT 0,064539 0,071268 0,073554 0,075971 0,077773 0,078009 0,078140 0,078589 0,078771 0,080012 0,079498 0,079320 0,080325 0,080244 0,080908 CT 24,496807 15,597140 12,334444 10,987662 9,766403 9,076478 8,718414 8,639395 8,160504 8,006253 7,796685 7,331805 7,596470 7,148420 7,242082 59 GT 24,496924 15,831477 12,711785 11,271803 10,269583 9,539273 9,235675 8,954391 8,632493 8,466980 8,288210 8,047796 8,001797 7,971463 7,835678 PPT 0,000087 0,000162 0,000200 0,000241 0,000287 0,000343 0,000404 0,000475 0,000554 0,000624 0,000691 0,000776 0,000857 0,000923 0,001001 TT 24,672050 16,021618 12,907177 11,467695 10,467982 9,739874 9,437225 9,155953 8,834029 8,671907 8,493088 8,253444 8,209347 8,179566 8,047227 Tabla 6.18: Resultados de ejecución (en sg.) de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.17: Gráficas de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) En el caso de la imagen del Lago St. Clair, el tiempo total de ejecución es similar al de la versión descrita en el apartado anterior. De hecho, los resultados obtenidos son ligeramente peores, debido principalmente al fenómeno denominado desbordamiento de registros o register spilling [37]. Este fenómeno ocurre cuando el número de registros máximos establecidos para cada thread es insuficiente para el kernel que se va a ejecutar. Esto se detecta con la salida de la compilación del código fuente si el flag -Xptxas=-v está activado. Este desborde provoca que los datos que no se han podido almacenar en los registros se almacenen en memoria local, con tiempos de accesos mayores, provocando una caı́da del rendimiento. Este factor también ocurrı́a en el procesamiento de la imagen Cuprite, pero se camuflaba con la penalización por las transferencias de datos entre host y dispositivo. (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.18: Gráficas de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) 60 IRT 0,005766 0,003949 0,003354 0,003585 0,003916 0,004093 0,004852 0,004644 0,003294 0,002645 0,004747 0,003410 0,004425 0,004371 0,004054 CAPÍTULO 6. RESULTADOS EXPERIMENTALES SKT 0,119411 0,119093 0,119802 0,117978 0,117239 0,115712 0,115354 0,114592 0,114661 0,114748 0,114685 0,114418 0,114579 0,114354 0,114557 BT 0,000002 0,009499 0,012754 0,015095 0,016693 0,017405 0,018444 0,019518 0,019313 0,020892 0,020548 0,021689 0,021613 0,022287 0,022265 ScT 0,315346 0,349727 0,367835 0,377176 0,389263 0,390930 0,382179 0,382582 0,398379 0,400883 0,404565 0,405705 0,408605 0,384867 0,388705 CT 102,720709 53,709227 37,831152 29,917336 25,106306 21,935792 19,688626 18,198350 16,778827 15,708724 14,736118 14,039456 13,378398 12,911301 12,712763 GT 102,720824 53,952885 38,226190 30,347664 25,562380 22,304449 20,157002 18,450896 17,012494 16,063433 15,191168 14,450360 13,755763 13,276968 12,867922 PPT 0,000088 0,000158 0,000199 0,000244 0,000287 0,000337 0,000401 0,000466 0,000554 0,000626 0,000694 0,000768 0,000854 0,000929 0,000999 TT 103,168611 54,441920 38,737827 30,868831 26,097683 22,841261 20,687371 18,981789 17,556516 16,610837 15,746574 15,005667 14,315817 13,813935 13,408644 Tabla 6.19: Resultados de ejecución (en sg.) de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) En cuanto al factor de aceleración conseguido para esta imagen de entrada, éste aumenta a aproximadamente un 8x, superior al conseguido para la misma imagen y la versión del algoritmo anterior. Esto sólo indica que el algoritmo escala ligeramente mejor, pero el tiempo de ejecución total empeora. Finalmente, para el procesamiento de la imagen más grande, Yellowstone, se consiguen los resultados expuestos en la tabla 6.20. En esta ocasión sı́ se percibe notablemente el empeoramiento del tiempo de ejecución del algoritmo provocado por el register spilling dado que la ejecución más rápida, correspondiente a la configuración de 15 nodos, es aproximadamente un 10 % más lenta que la versión basada en local memory. IRT 0,002804 0,004687 0,003829 0,003450 0,005021 0,004408 0,004499 0,004645 0,004167 0,004959 0,003865 0,004782 0,003480 0,003860 0,004119 SKT 0,117270 0,118609 0,116119 0,118544 0,118947 0,116944 0,114463 0,114452 0,114308 0,114780 0,114726 0,114786 0,114587 0,114519 0,114636 BT 0,000002 0,009424 0,012638 0,014911 0,016461 0,017628 0,018178 0,019169 0,019840 0,020801 0,020530 0,021579 0,021645 0,021768 0,022150 ScT 5,728717 5,059573 5,311343 6,972659 7,198395 7,202100 7,131761 6,923381 7,054056 7,111845 7,162059 7,192657 7,218305 7,294218 6,938815 CT 6,076623 826,619394 556,067588 417,029938 334,175347 281,098572 241,236472 211,685312 190,421293 171,752177 156,435695 144,534062 133,441804 124,812662 116,897165 GT 6,076696 831,547152 558,279981 418,844816 335,369278 281,895375 242,634762 212,607251 190,479745 171,916446 156,691119 144,535503 133,506242 124,814328 116,898942 PPT 0,000087 0,000158 0,000196 0,000241 0,000285 0,000329 0,000379 0,000452 0,000543 0,000607 0,000706 0,000803 0,000837 0,000914 0,001026 TT 11,959669 836,774550 563,760637 425,993195 342,746721 289,277675 249,941800 219,707373 197,716695 179,212621 164,037294 151,914091 140,907340 132,295992 124,025086 Tabla 6.20: Resultados de ejecución (en sg.) de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) Resumiendo los resultados obtenidos, las variaciones de la configuración de la GPU que se han introducido en esta versión no han dado los resultados esperados. Lejos de reducir el tiempo de ejecución del algoritmo, hemos provocado un comportamiento no deseado que ha empeorado los resultados. Para la siguiente versión, se eliminarán las mejoras introducidas en esta versión y se implementará una solución que haga uso de la memoria compartida de bloque o shared memory, una memoria a la que tienen acceso todos los hilos de un mismo bloque y de acceso muy rápido. Por otro lado, el fenómeno de register spilling no siempre significa una caı́da del rendimiento, por lo que en versiones sucesivas se impondrá un lı́mite de 20 registros por hilo para alcanzar la máxima de ocupación en la GPU. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS (a) Gráfica de CT y TT (en sg.) 61 (b) Gráfica de speedup Figura 6.19: Gráficas de la implementación con optimizaciones de compilación del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) 6.2.5. Implementación basada en memoria compartida Continuando con la evolución de la implementación del algoritmo PPI para clúster de GPUs, este apartado describe una nueva implementación basada en el uso de la memoria compartida por bloque o shared memory. Esta memoria es muy rápida en comparación con la memoria global de la GPU, pero por contra tiene un tamaño limitado (de 16 KB a 48 KB). El uso de memoria compartida pretende reducir las transferencias de memoria mediante la copia y compartición de un conjunto de pı́xeles de la imagen entre todos los hilos de un bloque. Cabe recordar que la jerarquı́a de memoria que ofrece CUDA se describió en el apartado 3.2.1 del capı́tulo 3. Para que una variable se almacene en memoria compartida o shared memory debe añadirse en su declaración el modificador de tipo introducido por CUDA llamado shared . Tal y como se describe en el apartado 3.2.2 del capı́tulo 3, este modificador provoca que la variable asociada resida en memoria compartida y sea accesible por todos los hilos de un mismo bloque durante el tiempo de vida de éste. El código 6.7 presenta la declaración de un array llamado sm pixels, que actuará como caché programática para que todos los hilos de un bloque accedan a un conjunto de pı́xeles de la imagen a procesar que se han almacenado previamente en dicho array, evitando que cada hilo de un bloque acceda directamente a la memoria global. El resto del kernel, que se muestra en el código 6.9 debe adaptarse para implementar la copia de los pı́xeles al vector sm pixels y posteriormente calcular las proyecciones al skewer asignado partiendo de los datos almacenados en éste. Cabe destacar el uso que se realiza de la función syncthreads() para establecer dos puntos de sincronización entre los hilos de un bloque a lo largo del kernel, a saber: El primer punto de sincronización se establece para que los hilos no accedan al espacio de memoria compartida antes de la copia de los pı́xeles de la imagen a procesar. El segundo punto de sincronización se establece para que no se actualice el contenido del array de memoria compartida con nuevos pı́xeles antes de que finalice el cálculo de la proyección de los pı́xeles actuales al skewer por los hilos del bloque. El tamaño del array sm pixels viene dado por el número de pı́xeles que puede almacenar, almacenado en una constante llamada N pixels, y por sus respectivos valores espectrales, indicado por la variable num bands, de ahı́ que su tamaño sea de N pixels * num bands (ver código 6.7). __shared__ float sm_pixels [ N_Pixels * num_bands ]; Código 6.7: Declaración de un array en memoria compartida El número de pı́xeles que se podrá almacenar en la memoria compartida está limitado por el tamaño máximo de dicha memoria que se asigna a cada bloque. Según la información que aporta la herramienta CUDA Occupancy Calculator, para una configuración de 512 hilos por bloque de GPU y 20 registros por hilo, los datos de ocupación son los mostrados en la tabla 6.21. 62 CAPÍTULO 6. RESULTADOS EXPERIMENTALES Hilos activos por multiprocesador Warps activos por Multiprocesador Bloques activos por Multiprocesador Ocupación de cada multiprocesador 1.536 48 3 100 % Tabla 6.21: Datos de ocupación de la GPU para la implementación MPI-CUDA basada en memoria compartida utilizados para el cálculo del tamaño del array a almacenar en dicho nivel de la jerarquı́a de memoria A la vista de estos datos, solamente 3 bloques estarán activos de forma simultánea en cada multiprocesador de la GPU, por lo que el lı́mite superior que se establece para el tamaño de la memoria compartida asignada a cada bloque será de un tercio del tamaño total disponible, el cual, dicho en cifras, es de 49152/3 = 16384 bytes. Sabiendo que cada pı́xel de la imagen ocupa 224 × 4 bytes = 896 bytes, el tamaño del array de memoria compartida será de b16384/896c = 18 pı́xeles. Por consiguiente los datos de ocupación de esta versión del algoritmo serán los mostrados en la tabla 6.22. Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 512 20 16.128 100 % Tabla 6.22: Datos de ocupación de la GPU para la implementación MPI-CUDA basada en memoria compartida del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT Continuando con la descripción del kernel implementado para esta versión del algoritmo PPI, cabe destacar que el proceso de la copia de los pı́xeles al array sm pixels, mostrado en el código 6.8, se realiza de forma paralela y favoreciendo la lectura de posiciones de memoria contiguas por hilos contiguos de un bloque (coalescencia). Como CUDA optimiza este tipo de lecturas para que se realicen en bloque y de una vez y la imagen está codificada en formato BIP [1], lo que significa que los valores espectrales de un mismo pı́xel en resolución espacial están almacenados de forma contigua en la imagen, para copiar un pı́xel al array sm pixels de forma paralela se utilizarán los 224 primeros hilos de un bloque, uno para cada valor espectral. Dado que estos hilos accederán a posiciones de memoria contiguas se realizará una lectura en bloque y se evitará la serialización de las transferencias a memoria para la copia del pı́xel. La introducción del bucle while se debe a que se ha querido implementar una versión genérica que funcione para tamaños de bloque menores al número de bandas de la imagen. En este caso, un mismo hilo copiarı́a dos valores espectrales al array de memoria compartida. // Copy pixels to SM int smidx = threadIdx . x ; while ( smidx < num_bands ) { for ( i = 0; i < N_Pixels ; i ++) { sm_pixels [ i * num_bands + threadIdx . x ] = image [( k * N_Pixels + i ) * num_bands + threadIdx . x ]; } smidx += blockDim . x ; } Código 6.8: Declaración de un array en memoria compartida Por último, y para terminar la descripción del kernel, se ha cambiado la estructura de almacenamiento de los resultados parciales del algoritmo y en esta ocasión no se almacenan por separado las coordenadas espaciales (x, y) en la estructura que almacena dichos resultados parciales. En esta ocasión y por simplicidad a la hora de procesar los pı́xeles, se almacena el ı́ndice de la pseudomatriz, el cual sigue la fórmula 6.2. i = x × num samples + y (6.2) Finalmente, el código completo del kernel para esta versión del algoritmo se muestra en el código 6.9. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS 63 int idx = blockDim . x * blockIdx . x + threadIdx . x ; __shared__ float sm_pixels [ N_Pixels * num_bands ]; if ( idx < num_skewers ) { float min = FLT_MAX , max = FLT_MIN , dot_product = 0.0; for ( int k = 0; k < num_lines * num_samples / N_Pixels ; k ++) { // Copy pixels to SM int smidx = threadIdx . x ; while ( smidx < num_bands ) { for ( int i = 0; i < N_Pixels ; i ++) { sm_pixels [ i * num_bands + threadIdx . x ] = image [( k * N_Pixels + i ) * num_bands + threadIdx . x ]; } smidx += blockDim . x ; } __syncthreads () ; for ( int i = 0; i < N_Pixels ; dotProduct (& skewers [ idx ] , dot_product ) ; if ( dot_product > max ) { max = dot_product ; local_results [ idx * 4 local_results [ idx * 4 } if ( dot_product < min ) { min = dot_product ; local_results [ idx * 4 local_results [ idx * 4 } } __syncthreads () ; i ++) { & sm_pixels [ i * num_bands ] , num_bands , & + 0] = k * N_Pixels + i ; // pixel + 1] = dot_product ; + 2] = k * N_Pixels + i ; // pixel + 3] = dot_product ; } } Código 6.9: Código del kernel del algoritmo PPI para la implementación basada en memoria compartida Una vez descrito el código de esta versión del algoritmo PPI para clúster de GPUs que hace uso de memoria compartida, se presentan los resultados obtenidos para las tres imágenes habituales. Comenzando por la imagen Cuprite, la tabla 6.23 muestra el promedio de los valores medidos en las ejecuciones realizadas para esta versión del algoritmo, con configuraciones de ejecución de 1 a 15 nodos de cómputo y seguidamente las figuras 6.20a y 6.20b muestran las gráficas de evolución del tiempo de cómputo (CT), del tiempo total de ejecución (TT) y del speedup según aumenta el número de nodos de cómputo utilizados. IRT 0,005262 0,005218 0,004950 0,004678 0,004728 0,003985 0,004224 0,003773 0,003133 0,004008 0,003993 0,002806 0,003309 0,003316 0,003804 SKT 0,098404 0,098699 0,097655 0,094231 0,093506 0,094902 0,094168 0,093351 0,093561 0,093341 0,093346 0,093504 0,093481 0,094660 0,093281 BT 0,000022 0,007945 0,010878 0,012992 0,014321 0,014957 0,015527 0,016503 0,016958 0,017809 0,017821 0,018348 0,018862 0,018189 0,019453 ScT 0,065236 0,071415 0,072741 0,076078 0,078608 0,076517 0,076589 0,078980 0,078724 0,080042 0,079544 0,079505 0,080159 0,079800 0,080709 CT 22,780076 14,614350 11,736442 10,535370 9,673711 9,109504 8,535719 8,327568 8,100804 7,669839 7,594869 7,626984 7,637773 7,392742 7,431989 GT 22,780158 14,863156 12,190816 10,858870 9,951118 9,432804 9,037139 8,741591 8,459683 8,390138 8,153650 7,996942 7,917867 7,937044 7,803767 PPT 0,000083 0,000148 0,000196 0,000231 0,000281 0,000333 0,000381 0,000446 0,000531 0,000611 0,000681 0,000750 0,000831 0,000895 0,000976 TT 22,955662 15,053010 12,384140 11,054094 10,149510 9,630272 9,235051 8,941845 8,659118 8,593363 8,356860 8,198552 8,121969 8,141672 8,010301 Tabla 6.23: Resultados de ejecución (en sg.) de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) 64 CAPÍTULO 6. RESULTADOS EXPERIMENTALES (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.20: Gráficas de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) Según se observa en los resultados, el tiempo de ejecución del algoritmo no ha disminuido respecto a versiones anteriores, pero tampoco empeora notablemente el tiempo de ejecución de la versión que hasta ahora arroja mejores resultados (local memory). Esto se debe principalmente a que la memoria compartida no deja de ser una caché controlada por el usuario que, además, comparte chip con la memoria caché L1, teniendo los mismos tiempos de acceso. Este uso de memoria compartida sı́ hubiese tenido más repercusión en el tiempo de ejecución para arquitecturas anteriores como la Tesla. Actualmente, con la jerarquı́a de caché que implementa la arquitectura Fermi, el uso de shared memory no es indispensable para disminuir el rendimiento. En esta ocasión el speedup es mejor que en la versión local memory, alcanzando un factor de casi 3x frente al 2,5x de ésta última. Tras los resultados obtenidos para la imagen de 44 MB, se muestran los resultados para la imagen del Lago St. Clair. Las gráficas 6.21a y 6.21b representan la evolución del tiempo de ejecución del algoritmo en cada nodo (CT) y el tiempo total del algoritmo (TT) ası́ como del speedup para cada configuración utilizada. Los resultados medidos se muestran en la tabla 6.24. Como cabı́a de esperar, los resultados obtenidos para esta imagen son similares a los mejores resultados obtenidos anteriormente para las implementaciones anteriores, volviendo a romper la barrera de los 13 segundos en el procesamiento de la imagen utilizando 15 nodos de cómputo. En cuanto al speedup alcanzado por esta versión del algoritmo se mantiene en 7x, pudiéndose ver claramente cómo este valor comienza a curvarse para las configuraciones que utilizan mayor número de nodos. IRT 0,004420 0,005210 0,005560 0,004449 0,005387 0,005346 0,004683 0,005096 0,004585 0,004979 0,003160 0,003974 0,004646 0,003807 0,004383 SKT 0,116810 0,115256 0,113412 0,115071 0,114137 0,116393 0,111495 0,111325 0,111195 0,111182 0,111646 0,111429 0,112449 0,111173 0,111731 BT 0,000025 0,009399 0,012691 0,015234 0,016257 0,017084 0,018059 0,019212 0,019536 0,020336 0,020427 0,021534 0,021533 0,022079 0,021788 ScT 0,301233 0,344011 0,375907 0,388081 0,382678 0,379939 0,375616 0,383012 0,391983 0,401689 0,406319 0,404548 0,412874 0,384849 0,388802 CT 93,368136 49,169154 34,924499 27,400518 23,373792 20,339352 18,484162 17,029876 15,725369 14,466571 13,945115 13,188010 12,639809 12,351217 11,765032 GT 93,368219 49,643279 35,318050 27,864872 23,856888 20,784989 18,897697 17,368439 15,997474 15,156156 14,427424 13,690499 13,048524 12,655579 12,330921 PPT 0,000079 0,000146 0,000190 0,000240 0,000288 0,000338 0,000417 0,000430 0,000541 0,000620 0,000684 0,000747 0,000829 0,000895 0,000957 TT 93,797492 50,124564 35,834759 28,395555 24,384787 21,313475 19,416531 17,896629 16,534284 15,704594 14,977763 14,241529 13,610668 13,188123 12,868784 Tabla 6.24: Resultados de ejecución (en sg.) de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) A la vista de los resultados anteriores, parece ser que los valores que se obtendrán para la imagen de mayor tamaño serán similares a los obtenidos por la implementación local memory, quedándo la duda de si se volverá a bajar de los 2 minutos de tiempo para procesar la imagen de los 4 GB. Los tiempos medidos para esta imagen se presentan en la tabla 6.24 y la evolución de los tiempos y el speedup frente al número de nodos se ilustra en las figuras 6.21a y 6.21b. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS (a) Gráfica de CT y TT (en sg.) 65 (b) Gráfica de speedup Figura 6.21: Gráficas de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) IRT 0,003944 0,003320 0,005074 0,004407 0,004547 0,004706 0,003989 0,004461 0,005583 0,005238 0,003606 0,004530 0,004721 0,005198 0,003925 SKT 0,115504 0,116279 0,114303 0,113276 0,113289 0,111320 0,114212 0,111377 0,111928 0,111284 0,111164 0,111405 0,113174 0,111576 0,111090 BT 0,000028 0,009476 0,012651 0,015229 0,016282 0,017472 0,018407 0,019188 0,019729 0,020596 0,020408 0,021266 0,021185 0,022011 0,022076 ScT 4,896240 5,392947 5,332101 7,033414 7,046309 7,153001 7,243374 6,999897 7,052209 7,078813 7,137345 7,175165 7,210193 7,275668 6,929979 CT 6,036655 752,254284 505,087797 379,299165 304,481140 254,845478 219,296014 193,593828 172,677596 156,328352 142,493207 130,631165 121,443850 112,807819 106,152012 GT 6,036707 753,106313 505,299801 380,183461 304,905104 255,065870 219,387866 193,594958 172,678770 156,329267 142,565007 130,666563 121,529069 112,809042 106,153305 PPT 0,000077 0,000150 0,000191 0,000236 0,000280 0,000328 0,000380 0,000426 0,000530 0,000619 0,000666 0,000719 0,000793 0,000850 0,001023 TT 11,088650 758,664694 510,802390 387,385765 312,125213 262,392399 226,808726 200,772298 179,914066 163,591343 149,880308 138,022905 128,924151 120,270764 113,266530 Tabla 6.25: Resultados de ejecución (en sg.) de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.22: Gráficas de la implementación basada en memoria compartida del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) De nuevo, tal y como se predijo, los resultados obtenidos para esta esta imagen de entrada son similares a los de la versión local memory, rompiendo de nuevo la barrera de los 2 minutos y procesando la imagen en 113 segundos. El speedup alcanzado con esta versión tampoco destaca frente al alcanzado anteriormente, superando por poco el factor 7x respecto a la ejecución de dos nodos (extrapolando unos 12,5x). En este punto parece que no se ha conseguido continuar con la mejora del tiempo de ejecución, principalmente porque los cambios introducidos no han supuesto un gran avance dado que la arquitectura Fermi se ha diseñado para corregir muchos de los pitfalls de las versiones anteriores de la arquitectura. En la siguiente implementación se aborda el problema de manera diferente, intentando mejorar el rendimiento mediante la adición de un nuevo nivel de paralelismo: el uso de varias GPUs. 66 6.2.6. CAPÍTULO 6. RESULTADOS EXPERIMENTALES Implementación multiGPU Desde la versión inicial, las versiones del algoritmo PPI para clúster de GPUs se han focalizado en reducir el tiempo de ejecución del algoritmo mediante técnicas basadas en la optimización de uso de la GPU comenzando por el uso de la memoria local del dispositivo, pasando por ajustes de compilación y finalizando por la explotación de los diferentes niveles de la jerarquı́a de memoria. Cierto es que desde la mejora notable del tiempo de ejecución introducida en la implementación basada en memoria local no se ha conseguido continuar con esta tendencia de evolución en las versiones posteriores, por lo que en esta ocasión se abordará el problema de manera diferente. Como se describe en el apartado 3.3 del capı́tulo 3, cada nodo de cómputo del clúster de GPUs de CETA-CIEMAT está equipado con dos GPUs. Hasta el momento solamente se habı́a utilizado una de ellas pero en la implementación a la que está dedicada esta sección se paralelizará el algoritmo PPI en un nivel más, repartiendo la carga de cada nodo de cómputo entre las GPUs que posee dicho clúster. El esquema de paralelización será el mismo que el de versiones anteriores, suponiendo que ambas GPUs trabajan como una sola, pero el doble de grande. Hasta ahora, cada hilo de la GPU se encargaba de calcular las proyecciones de los pı́xeles de la imagen de entrada sobre un skewer que se le habı́a asignado. En esta implementación, se dividirá el conjunto de skewers en dos, y cada GPU calculará las proyecciones de la imagen a procesar sobre la mitad del conjunto de skewers que se le ha asignado, lo que a efectos prácticos funciona como si de una de GPU con el doble de capacidad se tratase. Cierto es que se podrı́a haber dividido la imagen y posteriormente aunar los resultados, pero eso hubiese forzado a realizar una etapa adicional de generación intermedia de resultados para determinar cuáles de los pı́xeles de cada fragmento de la imagen asignado a cada GPU serı́an seleccionados como candidatos finales a endmember. La implementación multiGPU se basa en la implementación de la sección anterior basada en el uso de memoria compartida o shared memory. La configuración de hilos y bloques es la misma, y el número de pı́xeles a almacenar en memoria compartida no cambia (18 pı́xeles), dado que las dos GPUs de cada nodo de cómputo son iguales. Por lo tanto, los datos de ocupación obtenidos se mantienen de la versión anterior y se muestran en la tabla 6.26. Hilos por bloque Registros por hilo Memoria compartida por bloque (bytes) Ocupación de cada multiprocesador 512 20 16.128 100 % Tabla 6.26: Datos de ocupación de la GPU para la implementación MPI-CUDA multiGPU del algoritmo PPI para el entorno de Producción del clúster de CETA-CIEMAT La implementación multiGPU del algoritmo se basa en la duplicación de las acciones que se realizan para invocar al kernel de las versiones anteriores. CUDA proporciona la función cudaSetDevice(), que acepta como parámetro del ı́ndice de la GPU que se va a utilizar en cada momento, cuya misión es la de seleccionar el dispositivo con el que se quiere trabajar. En nuestro caso, como solamente hay dos GPUs por nodo, se realizarán invocaciones a dicha función con los parámetros 0 y 1. Puede parecer poco probable que un dispositivo esté equipado con dos GPUs, pero es un hecho que cada vez es más frecuente. Las tarjetas graficas de los últimos portátiles suelen tener dos GPUs, una integrada de potencia suficiente para el trabajo diario de un usuario medio, y una segunda más potente, que se utiliza cuando se ejecutan aplicaciones más exigentes como juegos y aplicaciones de edición de video entre otras, todo con la misión de alargar el tiempo de uso de la baterı́a. En cuanto a supercomputación, las tarjetas de la gama de NVidia Tesla que comienzan con S suelen estar compuestas por entre dos y cuatro tarjetas GPUs. El código 6.10 describe la invocación de un kernel para la GPU con identificador 1 de uno de los nodos de cómputo del clúster. Primeramente se selecciona el dispositivo a utilizar con la llamada a cudaSetDevice() para posteriormente comenzar con la reserva de memoria en el dispositivo y la trasferencia de datos del host a la GPU mediante el uso de las funciones cudaMalloc() y cudaMemcpy() para finalizar con la invocación del kernel ppi(). Cabe destacar que se hace uso de la función cudaFuncSetCacheConfig() con el flag cudaFuncCachePreferShared para indicarle a la GPU nuestra preferencia por el uso de memoria compartida frente a la caché L1. Una vez invocado el kernel, se podrı́a cambiar de dispositivo, dado que esta acción solamente se puede realizar cuando se realizan llamadas ası́ncronas de funciones CUDA, como por ejemplo la invocación de un kernel. Si el dispositivo estuviese ocupado con una operación bloqueante no se podrı́a realizar el cambio de GPU. 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS 67 Cabe destacar respecto al código en esta ocasión que la invocación del kernel ppi() se realiza pasándole la mitad del conjunto de skewers, de ahı́ que la estructura d skewers data1 se “rellene” con la segunda mitad del conjunto de skewers &skewers data[(num skewers / 2) * num bands]. cudaSetDevice (1) ; // Device memory p o i n t e r s float * d _image_c hunk1 ; float * d_ s ke we rs _ da ta 1 ; float * d _ l o c a l _ r e s u l t s 1 ; cudaMalloc (( void **) & d_image_chunk1 , ( chunk_size * num_samples * num_bands * sizeof ( float ) ) ) ; cudaMalloc (( void **) & d_skewers_data1 , ( num_skewers * num_bands * sizeof ( float ) ) ) ; cudaMalloc (( void **) & d_local_results1 , ( num_skewers * 2 * sizeof ( float ) ) ) ; // host -> device copy cudaMemcpy ( d_image_chunk1 , image_chunk , chunk_size * num_samples * num_bands * sizeof ( float ) , c u d a M e m c p y H o s t T o D e v i c e ) ; cudaMemcpy ( d_skewers_data1 , & skewers_data [( num_skewers / 2) * num_bands ] , ( num_skewers / 2) * num_bands * sizeof ( float ) , c u d a M e m c p y H o s t T o D e v i c e ) ; cudaMemcpy ( d_local_results1 , local_results , num_skewers * 2 * sizeof ( float ) , cudaMemcpyHostToDevice ); c u d a F u n c S e t C a c h e C o n f i g ( " ppi " , c u d a F u n c C a c h e P r e f e r S h a r e d ) ; // PPI kernel e x e c u t i o n ppi <<< num_bloques , num_hilos > > >( d_image_chunk1 , d_skewers_data1 , d_local_results1 , chunk_size , num_samples , num_bands , num_skewers / 2) ; Código 6.10: Código de la invocación del kernel del algoritmo PPI para una GPU en la implementación multiGPU Una vez que se han invocado ambos kernels se tiene que establecer un punto de sincronización para que ambos dispositivos terminen sus tareas antes de recoger los resultados. Esta acción se realiza mediante la llamada a la función proporcionada por CUDA cudaDeviceSynchronize() en cada dispositivo (ver código 6.11) // Device 1 p r e v i o u s l y s e l e c t e d c u d a D e v i c e S y n c h r o n i z e () ; cudaSetDevice (0) ; c u d a D e v i c e S y n c h r o n i z e () ; Código 6.11: Sincronización de los dispositivos en la implementación multiGPU del algoritmo PPI Para finalizar, se deben recolectar los resultados parciales generados por cada GPU. Esta recolección es muy sencilla, dado que simplemente éstos se deben copiar adecuadamente en la estructura que se devolverá al nodo maestro. Los resultados de la GPU 0 se almacenarán en la primera mitad de la estructura que almacena los resultados (local results) y los de la GPU 1 se almacenarán en la segunda mitad. El proceso de recolección de resultados para el dispositivo 1 se muestra en el código 6.12. cudaSetDevice (1) ; // device -> host copy cudaMemcpy (& local_results [ num_skewers * 2] , d_local_results1 , num_skewers * 2 * sizeof ( float ) , c u d a M e m c p y D e v i c e T o H o s t ) ; // Free memory cudaFree ( d _s ke w er s_ da t a1 ) ; cudaFree ( d_image _chunk1 ) ; cudaFree ( d _ l o c a l _ r e s u l t s 1 ) ; Código 6.12: Recolección de los resultados de una GPU en la implementación multiGPU Una vez descrito el código se presentan los resultados obtenidos para las imágenes habituales. Comenzando por la imagen Cuprite, la tabla 6.27 muestra el promedio de los tiempos medidos para cada uno de los parámetros descritos en el apartado 6.1.2 y las figuras 6.23a y 6.23b muestran las gráficas de evolución del tiempo de cómputo (CT) y el tiempo total (TT) de ejecución, ası́ como el speedup, para las configuraciones ejecutadas de 1 a 15 nodos. 68 CAPÍTULO 6. RESULTADOS EXPERIMENTALES IRT 0,006409 0,005415 0,004714 0,005132 0,004603 0,005060 0,005135 0,005378 0,004209 0,004241 0,005212 0,004618 0,005581 0,004976 0,005046 SKT 0,096049 0,096511 0,094594 0,094273 0,096524 0,093576 0,094045 0,093725 0,093919 0,093545 0,093454 0,093843 0,093700 0,093973 0,093635 BT 0,000002 0,007996 0,010657 0,012737 0,014094 0,014964 0,015625 0,016348 0,016988 0,017822 0,018066 0,024836 0,018551 0,018637 0,025065 ScT 0,443146 0,067120 0,070123 0,072812 0,075562 0,073651 0,074613 0,076068 0,076698 0,077848 0,077813 0,077357 0,078124 0,078435 0,079037 CT 15,793712 11,375779 9,734920 8,873116 8,265920 8,017330 8,041177 8,100983 7,720451 7,847921 7,552571 7,540006 7,260395 7,078283 6,973607 GT 15,793785 11,723104 10,303225 9,436983 9,015941 8,880147 8,601562 8,647567 8,716753 8,602033 8,494860 8,292043 8,370715 8,339936 8,141262 PPT 0,000076 0,000142 0,000188 0,000231 0,000287 0,000325 0,000387 0,000431 0,000530 0,000591 0,000660 0,000738 0,000796 0,000857 0,000907 TT 16,347264 11,907246 10,490626 9,631075 9,214113 9,075537 8,799135 8,848130 8,916897 8,803820 8,698847 8,502028 8,577265 8,546037 8,354137 Tabla 6.27: Resultados de ejecución (en sg.) de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.23: Gráficas de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Cuprite) A la vista de los resultados se puede comprobar que el tiempo de ejecución para la imagen de Cuprite no ha mejorado. Esto es debido a que el tiempo de procesamiento de la imagen es muy pequeño (aproximadamente 1 sg.) respecto del tiempo de transferencia de los datos de entrada entre la CPU y los dispositivos. El tiempo empeora respecto a las versiones anteriores porque en esta ocasión se realiza una copia de los datos de entrada por cada GPU. Este comportamiento del algoritmo deberı́a cambiar según aumente el tamaño de la imagen dado que el procesamiento de la imagen tomará peso con respecto a las comunicaciones. A continuación se muestran los resultados obtenidos para la imagen del Lago St. Clair. La tabla 6.28 presenta el promedio de los tiempos de ejecución medidos para cada uno de los parámetros descritos en el apartado 6.1.2 del presente capı́tulo y las figuras 6.24a y 6.24b muestran la evolución de los tiempos de ejecución y del speedup. En esta ocasión se ha conseguido reducir el tiempo de ejecución obtenido por las versiones anteriores del algoritmo, siendo éste de apenas 11 sg. para la configuración de ejecución que utiliza 15 nodos de cálculo. El speedup conseguido no es muy bueno ya que apenas alcanza el factor 5x. Para finalizar con la presentación de las pruebas, a continuación se incluyen los resultados obtenidos para la imagen de Yellowstone. Los datos medidos y las gráficas generadas se detallan en la tabla 6.29 y las figuras 6.25a y 6.25b respectivamente. Al igual que en el caso de la imagen del Lago St. Clair, se ha conseguido reducir el tiempo de ejecución del algoritmo para la imagen más grande, llegándose a procesar en poco más de 1 minuto (67 sg.). Esto es debido a que el grueso del tiempo de cómputo se debe al procesamiento de la imagen en lugar que a las transferencias de datos como ocurrı́a en la imagen de Cuprite. En cuanto al speedup conseguido es menor que en las versiones anteriores del algoritmo, alcanzándose un factor de aceleración de poco más de 6x respecto a la ejecución de dos nodos (extrapolando no alcanzarı́a 6.2. DESARROLLO DE PPI PARA EL CLÚSTER DE GPUS IRT 0,006436 0,005324 0,004581 0,005628 0,005631 0,004706 0,005861 0,005091 0,004802 0,005363 0,003899 0,005253 0,004506 0,005976 0,004926 SKT 0,116302 0,116349 0,115191 0,111039 0,112410 0,114535 0,114400 0,111301 0,111134 0,111431 0,111558 0,111432 0,111268 0,111469 0,111206 BT 0,000002 0,009385 0,012753 0,014791 0,016506 0,017540 0,018292 0,019552 0,019702 0,020586 0,020831 0,021458 0,021757 0,022317 0,022328 ScT 0,311541 0,353297 0,376247 0,377138 0,382188 0,386792 0,374357 0,382294 0,393246 0,397855 0,401974 0,404582 0,408799 0,382669 0,389060 CT 53,010888 29,594654 22,086820 18,264652 15,940279 13,991949 13,408163 12,695689 11,659171 11,265264 10,727219 10,498291 10,049843 10,070015 9,824852 69 GT 53,010970 30,054039 22,697393 18,883179 16,439741 14,699043 13,927808 13,146160 12,513935 12,032946 11,727650 11,318091 10,903699 10,918805 10,658089 PPT 0,000078 0,000148 0,000193 0,000231 0,000282 0,000337 0,000382 0,000420 0,000545 0,000609 0,000665 0,000741 0,000895 0,000887 0,000958 TT 53,462331 30,546396 23,214768 19,400933 16,965877 15,231295 14,450857 13,673939 13,052215 12,578863 12,275116 11,871989 11,459992 11,453097 11,197010 Tabla 6.28: Resultados de ejecución (en sg.) de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.24: Gráficas de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Lago St. Clair ) IRT 0,004765 0,004829 0,004821 0,004712 0,005245 0,005303 0,004257 0,005388 0,004453 0,005200 0,004817 0,004294 0,003639 0,003179 0,004085 SKT 0,115346 0,115492 0,113272 0,111370 0,112329 0,111157 0,111370 0,111551 0,111483 0,111300 0,111163 0,111898 0,111355 0,111411 0,111874 BT 0,000002 0,009512 0,012670 0,014534 0,016562 0,017592 0,018763 0,019235 0,019945 0,021122 0,027526 0,021577 0,021346 0,022161 0,022592 ScT 4,968464 5,007724 5,243812 6,739466 7,232359 7,137247 7,175867 6,923678 7,022828 7,094025 7,147466 7,176949 7,212339 7,279546 6,922418 CT 6,287983 401,900796 270,236191 203,789313 165,335666 137,943963 119,424813 105,774022 94,853668 86,393971 78,968023 73,132041 67,696651 63,763476 59,463695 GT 6,288034 401,901125 270,236587 203,789783 165,336192 138,196509 119,437964 105,939544 94,908862 86,394878 78,968999 73,236507 67,884871 63,883725 59,692932 PPT 0,000077 0,000148 0,000191 0,000237 0,000277 0,000323 0,000369 0,000422 0,000542 0,000603 0,000685 0,000726 0,000812 0,000862 0,000929 TT 11,422122 407,079036 275,654485 210,696289 172,743685 145,508139 126,786683 113,045920 102,110127 93,669184 86,304342 80,595950 75,278965 71,343455 66,799286 Tabla 6.29: Resultados de ejecución (en sg.) de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) los 10x). En este punto podemos considerar satisfactorios los resultados obtenidos para procesar este tipo de 70 CAPÍTULO 6. RESULTADOS EXPERIMENTALES (a) Gráfica de CT y TT (en sg.) (b) Gráfica de speedup Figura 6.25: Gráficas de la implementación multiGPU del algoritmo PPI para clúster de GPUs (Imagen: Yellowstone) imágenes, llegando al punto de haber conseguido procesar una imagen de 4 GB en apenas un minuto. Probablemente se pueda optimizar esta implementación multiGPU para conseguir una reducción mejor, pero se propondrá para un trabajo futuro. En el siguiente apartado se realizará una comparativa de los mejores resultados obtenidos con la implementación para clústers de GPU frente a implementaciones para otras arquitecturas. 6.3. Comparativa frente a otras implementaciones Una vez que se ha desarrollado la implementación del algoritmo PPI para un clúster de GPUs y tienen los resultados de ejecución para el conjunto de imágenes reales capturadas por el sensor AVIRIS procedemos a comparar los tiempos de ejecución con los obtenidos para la versión secuencial, para clústers de computadores y tarjetas GPUs con el ánimo de estudiar cómo evolucionan los tiempos de ejecución y los factores de aceleración. Los parámetros de entrada serán los mismos que se han utilizado para la ejecución de las pruebas anteriores. A modo de recordatorio se enumeran a continuación: Una imagen hiperespectral de entrada: Se ejecutarán las pruebas para las tres imágenes del sensor AVIRIS pertenecientes a las regiones de Cuprite, Lago St. Clair y Yellowstone. Un número de iteraciones o tamaño del conjunto de skewers: Este número se establecerá a 15.360 para que coincida con las pruebas ejecutadas anteriormente. Una configuración de número de bloques e hilos de GPU : Estos parámetros solamente son necesarios para las versiones que utilizan GPUs y se establecen para maximizar la ocupación del dispositivo y, consecuentemente, mejorar el rendimiento. El número de bloques se establece a 30 y el de hilos a 512. En cuanto a las opciones de compilación utilizadas, todas las versiones del algoritmo se han compilado con la opción -O3, el cual establece el nivel de optimización del binario generado al máximo. La plataforma utilizada para las pruebas ha sido el clúster de GPUs de CETA-CIEMAT. En primer lugar se comparan los tiempos de ejecución obtenidos para las versiones del algoritmo antes mencionadas utilizando la imagen de entrada Cuprite. La tabla 6.30 presenta los resultados en segundos para cada una de las versiones sujetas a estudio junto con el factor de aceleración obtenido respecto de la versión secuencial. Tiempo (en sg.) Factor de aceleración Secuencial 1319,944515 1x Clúster 92,411848 14,28x GPUs 25,810000 51,14x Clúster de GPUs 8,354137 157,99x Tabla 6.30: Comparativa de resultados obtenidos para las versiones secuencial, clúster de computadores, GPUs y clúster de GPUs en el procesamiento de la imagen Cuprite A la vista de los resultados se puede afirmar que la versión que mejor factor de aceleración obtiene es la implementación para clústers de GPUs con un factor de 158x. Cabe destacar también que la versión para GPUs consigue procesar la imagen de Cuprite en menos de un minuto. Por otro lado, la 6.3. COMPARATIVA FRENTE A OTRAS IMPLEMENTACIONES 71 versión para clúster de computadores, aún utilizando 15 nodos de cálculo, procesa la imagen en más de 90 segundos. Tras los buenos resultados obtenidos para la imagen más pequeña, se procede a presentar los resultados obtenidos para la imagen de tamaño mediano, correspondiente al Lago St. Clair. Se espera que el factor de aceleración mejore debido a que el tiempo medido para la imagen Cuprite se ve altamente influenciado por la transferencia de datos y la comunicación entre nodos, como se describe en el apartado anterior. Los tiempos medidos para cada una de las implementaciones y el factor de aceleración calculado se muestran en la tabla 6.31. Tiempo (en sg.) Factor de aceleración Secuencial 5018,109371 1x Clúster 282,842483 17,74x GPUs 91,340000 54,94x Clúster de GPUs 11,197010 448,17x Tabla 6.31: Comparativa de resultados obtenidos para las versiones secuencial, clúster de computadores, GPUs y clúster de GPUs en el procesamiento de la imagen Lago St. Clair Como era de esperar, el factor de aceleración para la versión de clúster de GPUs vuelve a aumentar 448,17x, debido principalmente a que el mayor tamaño de la imagen del Lago St. Clair permite hacer notar el uso de dos GPUs para el procesamiento de dicha imagen. Cabe destacar también que el resto de factores de aceleración también aumenta. Por último se presentan los resultados obtenidos para la imagen de mayor tamaño, perteneciente al Parque Yellowstone. A priori los resultados deberı́an mostrar un aumento del factor de aceleración, tal y como ocurrı́a con la imagen del Lago St. Clair. Los resultados obtenidos para cada versión del algoritmo y sus respectivos factores de aceleración se presentan en la tabla 6.32. Tiempo (en sg.) Factor de aceleración Secuencial 83316,717722 1x Clúster 5834,629652 14,27x GPUs N/A N/A Clúster de GPUs 66,799286 1262,37x Tabla 6.32: Comparativa de resultados obtenidos para las versiones secuencial, clúster de computadores, GPUs y clúster de GPUs en el procesamiento de la imagen Yellowstone En esta ocasión, la implementación del algoritmo PPI para clúster de GPUs ha alcanzado el valor máximo de factor de aceleración, siéndo éste de 1262,37x. Esto quiere decir que se ha conseguido procesar una imagen que el algoritmo secuencial procesa en 23 horas en menos poco más de 1 minuto. Este resultado aporta mucho valor dado que con la implementación del algoritmo PPI para clúster de GPUs se acelerarı́a notablemente el procesamiento de grandes volúmenes de datos hiperespectrales y se evitarı́a que parte de esos datos acabasen almacenados o desechados sin que se pudiese trabajar con ellos. Por otro lado, el procesamiento de la imagen de Yellowstone no ha sido posible en la GPU porque, al igual que ocurrı́a con las implementaciones para clúster de GPUs que usaban un solo nodo de cómputo, la memoria de la ésta no ofrecı́a espacio suficiente para almacenar la imagen en dicho dispositivo. En este punto, finalizada la presentación y el análisis de los resultados experimentales para las diferentes arquitecturas paralelas consideradas y a la vista del buen rendimiento obtenido por la versión de PPI para clúster de GPUs podemos seguir afirmando que ésta resulta una buena alternativa a las implementaciones existentes como la incluı́da en ENVI para el análisis de imágenes hiperespectrales reales. En el siguiente capı́tulo se resumen las conclusiones obtenidas a partir del trabajo realizado y se presentan una serie de propuestas de trabajo futuro para una posible continuación de esta lı́nea de investigación. 72 CAPÍTULO 6. RESULTADOS EXPERIMENTALES Capı́tulo 7 Conclusiones y trabajo futuro A lo largo de esta memoria se ha presentado la evolución del desarrollo de una versión del algoritmo de extracción de firmas espectrales puras PPI para un clúster de GPUs. Además se ha realizado un estudio comparativo de las diferentes evoluciones de dicho algoritmo, comenzando por la versión inicial, la cual no implementa ningún mecanismo de optimización del rendimiento, y finalizando por la implementación que hace uso de varias tarjetas GPUs simultáneamente. Además se ha realizado una comparativa de tiempos de ejecución con otras implementaciones del algoritmo, véase, una versión secuencial, otra para un clúster de computadores y una tercera para GPUs. Para todas las pruebas realizadas se ha utilizado un conjunto de imágenes reales de diferentes tamaños, moviéndonos en un rango de tamaño de imagen desde decenas de Megabytes hasta varios Gigabytes, capturadas por el sensor AVIRIS del Jet Propulsion Laboratory de NASA. Como resultado se ha obtenido un estudio detallado de la aplicación de una arquitectura de última generación de computación de alto rendimiento como es un clúster de GPUs para el procesamiento de análisis hiperespectrales, realizando una contribución al estado del arte en la materia dada la reciente aparición de este tipo de infraestructuras. Las principales aportaciones del presente estudio pueden resumirse en un conjunto de contribuciones que se presentan a continuación: Tras el análisis de los resultados obtenidos por la implementación del algoritmo PPI para clúster de GPUs, obteniéndose factores de aceleración superiores a 1200x para el procesamiento de imágenes de gran tamaño respecto su versión secuencial, podemos afirmar que a dı́a de hoy los clúster de GPUs pueden ser aplicados de forma satisfactoria en el procesamiento de imágenes hiperespectrales. Esta arquitectura de cómputo puede ser muy adecuada para procesar datos almacenados en repositorios digitales, pudiéndose obtener información de datos históricos muy relevantes para disciplinas como la meteorologı́a, medio ambiente, estudios demográficos y biologı́a entre otros. A lo largo de este trabajo, se ha realizado un estudio de diferentes técnicas de optimización de rendimiento para la computación GPU, intentando aprovechar al máximo el modelo de ejecución y de memoria que implementan las tarjetas de NVidiaTM , pudiendo afirmar que esta compañı́a está realizando un gran esfuerzo en la optimización automática de rendimiento de aplicaciones para GPUs, especialmente en lo relacionado con las operaciones con la memoria, para descargar de esta tarea a los desarrolladores. Ası́ mismo, se ha conseguido aprovechar las capacidades de cómputo de los nodos del clúster de computación de CETA-CIEMAT al implementar un algoritmo que es capaz de trabajar con las dos GPUs instaladas cada uno de dichos nodos. Además, tal y como se ha comentado a lo largo de esta memoria, el acceso a recursos hı́bridos de cómputo como los clústers de GPUs es una opción relativamente sencilla, debido principalmente a que estas infraestructuras son cada vez más comunes por su moderado coste económico y a que los proveedores de recursos computacionales en la nube como Amazon permiten la configuración de un clúster de estas caracterı́sticas pagando solamente por el uso realizado del mismo. De hecho, durante la escritura de esta memoria Amazon ha publicado que sus instancias de GPUs para clústeres ya están disponible en su centro de datos localizado en Irlanda a un precio de 2,36 Dólares la hora. 73 74 CAPÍTULO 7. CONCLUSIONES Y TRABAJO FUTURO Para finalizar el capı́tulo se propone una serie de posibles tareas que permitirán continuar la lı́nea de trabajo presentada en esta memoria: Implementar otros algoritmos de extracción de endmembers no interactivos, como OSP y NFINDR, con vistas a automatizar el procesamiento de datos históricos hiperespectrales sin la intervención de un usuario. Evolucionar la implementación multiGPU del algoritmo PPI para soportar un número indeterminado de GPUs, de diferente potencia computacional, aplicando técnicas de computación heterogénea al paradigma de la computación multiGPU. Evaluar las prestaciones del algoritmo PPI para clúster de computadores de cara a otras arquitecturas paralelas no estudiadas en este trabajo como los sistemas de memoria compartida. Determinar el grado de influencia de los generadores de números aleatorios en los resultados del algoritmo PPI, dado que se ha comprobado durante este trabajo que los resultados cambian según el generador utilizado. Como conclusión final, comentar que los resultados de las pruebas realizadas para la fundamentación de esta memoria han superado las expectativas iniciales y que éste es un campo que no queda cerrado a su futura investigación, fin para el cual se han señalado algunos pasos propuestos a seguir en el futuro. Bibliografı́a [1] Extendable Image Formats for ArcView GIS (http://downloads.esri.com/support/whitepapers/other /eximgav.pdf). July 1999. Page 8. 3.1 ESRI and 3.2 White Paper. [2] NVIDIA CUDA Programming Guide 4.2. NVidia Corporation, 2012. [3] MPICH library for message passing. http://www-unix.mcs.anl.gov/mpi/mpich/. [4] CUDA and Fermi update. NVidia Corporation, Virtual Summer School of Computational Science and Engineering 2012. https://www.youtube.com/watch?v=za8 KNN2-fQ [5] G. Amdahl. Validity of the single processor approach to achieving large- scale computing capabilities. AFIPS Conference Proceedings, 40:483–485, 1967. [6] C.A. Bateson, B. Curtis, “A method for manual endmember selection and spectral unmixing,” Remote Sensing of Environment, vol. 55, pp.229–243, 1996. [7] J. W. Boardman, F. A. Kruse, and R. O. Green. Mapping target signatures via partial unmixing of aviris data. In Proceedings JPL Airborne Earth Science Workshop, pages 23–26, Pasadena, California, 1995. [8] R. Brightwell, L. Fisk, D. Greenberg, T. Hudson, M. Levenhagen, A. Maccabe, and R. Riesen. Massively parallel computing using commodity components. Parallel Computing, 26:243–266, 2000. [9] C.-I. Chang. Hyperspectral imaging: Techniques for spectral detection and classification. Kluwer Academic/Plenum Publishers: New York., 2003. [10] C.-I. Chang. Estimation of number of spectrally distinct signal sources in hyperspectral imagery. IEEE Transactions on Geoscience and Remote Sensing, 42(3):608–619, 2004. [11] C.-I Chang and A. Plaza. A Fast Iterative Algorithm for Implementation of Pixel Purity Index. IEEE Geoscience and Remote Sensing Letters, vol. 3, no. 1, pp. 63- 67, January 2006. [12] M. Flynn. Some computer organizations and their effectiveness. IEEE Transactions on Computers, 21(9):948–960, September 1972. [13] M. Galassi et al, GNU Scientific Library Reference Manual (2nd Ed.), ISBN 0954161734, 2006 http://www.gnu.org/software/gsl/ [14] A. F. H. Goetz and B. Kindel. Comparison of unmixing result derived from aviris, high and low resolution, and Hydice images at Cuprite, NV. In Proceedings IX NASA/JPL Airborne Earth Science Workshop, Pasadena, California, 1999. [15] R. O. Green and J. Boardman. Exploration of the relationship between information content and signal-to-noise ratio and spatial resolution in aviris spectral data. In Proceedings IX NASA/JPL Airborne Earth Science Workshop, Pasadena, California, 2000. [16] R. O. Green, M. L. Eastwood, C. M. Sarture, T. G. Chrien, M. Aronsson, B. J. Chippendale, J. A. Faust, B. E. Pavri, C. J. Chovit, M. Solis, M. R. Olah, and Orlesa Williams. Imaging spectroscopy and the airborne visible/infrared imaging spectrometer (aviris). Remote Sensing of Environment, 65:227–248, 1998. 75 76 BIBLIOGRAFÍA [17] J. C. Harsanyi and C.-I. Chang, Hyperspectral image classification and dimensionality reduction: An orthogonal subspace projection. IEEE Transactions on Geoscience and Remote Sensing, vol. 32, no. 4, pp. 779–785. [18] G. Healey and D. Slater. Models and methods for automated material identification in hyperspectral imagery acquired under unknown illumination and atmospheric conditions. IEEE Transactions on Geoscience and Remote Sensing, vol. 37, pp. 2706-2717, 1999. [19] D. Landgrebe. Multispectral Data Analysis, A Signal Theory Perspective. Springer Hoboken, NJ, 1998. [20] D. Landgrebe. Hyperspectral image data analysis. IEEE Signal Processing Magazine, 19(1):17–28, 2002. [21] A. Lastovetsky and J. Dongarra. High-Performance Heterogeneous Computing. New York: Wiley, 2009. [22] D. Lavenier, E. Fabiani, S. Derrien, C. Wagner. Systolic array for computing the pixel purity index (PPI) algorithm on hyper spectral images. SPIE Conference on Imaging Spectrometry, San Diego, CA, USA, 2001. [23] F. Liu, F. Seinstra, and A. Plaza. Parallel hyperspectral image processing on multi-cluster systems. SPIE Journal of Applied Remote Sensing, 2011. [24] E. Lusk, N. Doss, and A. Skjellum. A high-performance, portable implementation of the MPI message passing interface standard. Parallel Computing, 22:789–828, 1996. [25] M. Matsumoto and T. Nishimura. 1998. Mersenne twister: a 623-dimensionally equidistributed uniform pseudo-random number generator. ACM Trans. Model. Comput. Simul. 8, 1 (January 1998), 3-30. [26] A. F. Paz. Diseño e implementación de algoritmos paralelos de análisis de imágenes hiperespectrales en tarjetas gráficas programables. Tesis doctoral. Universidad de Extremadura, 2011. [27] R.M. Pérez, P. Martı́nez, A. Plaza, P.L. Aguilar. Systolic Array Methodology for a Neural Model to Solve the Mixture Problem. Neural Networks and Systolic Array Design. Edited by D. Zhang and S.K. Pal. World Scientific, 2002. [28] A. Plaza, C.-I Chang. Clusters Versus FPGA for Parallel Processing of Hyperspactral Imagery. The International Journal of High Performance Computing Applications, vol. 22 – no. 1 – pp.1-7, 2008. [29] A. Plaza, P. Martı́nez, R.M. Pérez, J. Plaza. Spatial/Spectral Endmember Extraction by Multidimensional Morphological Operations. IEEE Transactions on Geoscience and Remote Sensing, vol. 40, no. 9, pp. 2025-2041, 2002. [30] A. Plaza, J. Plaza, and A. Paz. Parallel heterogeneous CBIR system for efficient hyperspectral image retrieval using spectral mixture analysis. Concurrency and Computation: Practice & Experience, 9:1138–1159, June 2010. [31] A. Plaza, J. Plaza, and D. Valencia. Impact of platform heterogeneity on the design of parallel algorithms for morphological processing of high-dimensional image data. The Journal of Supercomputing, 40(1):81–107, 2007. [32] A. Plaza, J. Plaza, D. Valencia. AMEEPAR: Parallel Morphological Algorithm for Hyperspectral Image Classification in Heterogeneous Networks of Workstations.” Lecture Notes in Computer Science, vol. 3391, pp. 888-891, 2006. [33] A. Plaza, D. Valencia, J. Plaza and C.-I Chang. Parallel Implementation of Endmember Extraction Algorithms from Hyperspectral Data. IEEE Geoscience and Remote Sensing Letters, vol. 3, no. 3, pp. 334-338, July 2006. [34] H. Ren, C.-I Chang, Automatic spectral target recognition in hyperspectral imagery. IEEE Transactions on Aerospace and Electronic Systems, vol. 39, no. 4, pp. 1232-1249, 2003. BIBLIOGRAFÍA 77 [35] S. Sanchez y A. Plaza. Implementación paralela del algoritmo Pixel Purity Index para análisis hiperespectral en GPUs. Jornadas de Paralelismo, Congreso Español de Informática (CEDI’2010), Valencia, Spain, 2010. [36] S. Sanchez.Implementación de algoritmos de análisis hiperespectral en tarjetas gráficas programables (GPUs). Trabajo Fin de Máster MUIT-TINC. Universidad de Extremadura, Julio 2010. [37] J. Sanders and E. Kandrot. CUDA by Example: An Introduction to General-Purpose GPU Programming. Addison-Wesley Educational Publishers Inc; First edition. ISBN 0131387685. 2010. [38] R. A. Schowengerdt. Remote Sensing: Models and Methods for Image Processing. Academic Press: London, 1997. [39] J. Setoain, M. Prieto, C. Tenllado, A. Plaza, F. Tirado. Parallel Morphological Endmember Extraction Using Commodity Graphics Hardware. IEEE Geoscience and Remote Sensing Letters, vol. 43, no. 3, pp. 441-445, 2007. [40] V. S. Sunderam. PVM: A framework for parallel distributed computing. Concurrency and Computation: Practice & Experience, 2(4):315–339, December 1990. [41] B. Thai, G. Healey, D. Slater. Invariant subpixel material identification in AVIRIS imagery. Proc. JPL AVIRIS workshop, JPL Publication 99-17, 1999. 78 BIBLIOGRAFÍA Publicaciones del candidato Presentaciones en congresos: • S. Garcı́a, J. M. Franco, C. Suárez, G. Dı́az and A. Plaza. Developing a portlet for the GISELA Science Gateway to process hyperspectral images. Joint GISELA-CHAIN Conference, Mexico City, 2012. • J. M. Franco and A. Plaza. Parallel implementation of the Pixel Purity Index algorithm for GPU clusters. 27th IEEE International Parallel & Distributed Processing Symposium, Boston, Massachusetts, 2013. (Pendiente de aceptación). 79