Full text
Trabajo Fin de Grado en Grado de Ingeniería de Computadores Evaluación del rendimiento de un planificador de tareas sobre una plataforma heterogénea ARM+DSP de Texas Instruments Realizado por Bermúdez Blanco, Javier Director Igual Peña, Francisco Daniel
Trabajo Fin de Grado en Ingeniería de Computadores Autorización de difusión El abajo firmante Javier Bermúdez Blanco, alumno en el Grado de Ingeniería de Computadores, autoriza a la Universidad Complutense de Madrid (UCM) a difundir y utilizar con fines académicos, no comerciales y mencionando a su autor, el presente Trabajo de Fin de Grado: “Evaluación del rendimiento de un planificador de tareas sobre una plataforma heterogénea ARM+DSP de Texas Instruments”, realizado durante el curso académico 2015-2016, bajo la dirección de Francisco Igual Peña. Así mismo autoriza a la Universidad Complutense de Madrid a que sea depositado en acceso abierto en el repositorio institucional e-prints complutense con el objeto de incrementar la difusión, uso e impacto del TFG en Internet y garantizar su preservación y acceso a largo plazo. 2 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Índice de contenidos Palabras clave........................................................................................................5 Keywords...............................................................................................................6 Resumen................................................................................................................7 Abstract.................................................................................................................7 1 Introducción y objetivos.......................................................................................9 Introduction and goals.......................................................................................10 Objetivos generales...........................................................................................12 2 Modelos de programación para arquitecturas heterogéneas...................................13 CUDA...............................................................................................................13 Características...............................................................................................13 Limitaciones..................................................................................................14 CUDA como modelo de programación............................................................14 Jerarquía de threads......................................................................................16 Espacios de memoria.....................................................................................17 Ejemplo de código CUDA...............................................................................18 OpenCL............................................................................................................19 Características...............................................................................................19 Programación en OpenCL...............................................................................20 Jerarquía de threads......................................................................................20 Gestión de memoria......................................................................................21 OmpSs..............................................................................................................24 Modelo de programación...............................................................................25 Planificador de tareas (runtime).....................................................................31 Ventajas e inconvenientes de cada modelo de programación............................31 Ejemplos de códigos OmpSs...........................................................................32 3 Problema objetivo. Detección de bordes..............................................................33 Descripción del algoritmo y motivación..............................................................33 Etapas..........................................................................................................33 Procesamiento por bloques. Descripción de las tareas..........................................34 Etapas..........................................................................................................34 Implementación utilizando OmpSs......................................................................37 Visión general de la implementación..............................................................38 Paralelismo a nivel de tareas y dependencias de datos....................................39 Esquema algorítmico y detalles de implementación..........................................40 4 Resultados experimentales..................................................................................45 Descripción de las arquitecturas objetivo............................................................45 Descripción de las CPU Intel Xeon (Bujaruelo)................................................45 Descripción de las GPU (Bujaruelo)................................................................45 Descripción de las CPU ARM (K2H)................................................................46 3 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Descripción del DSP (K2H).............................................................................46 Resultados experimentales y análisis...................................................................47 Utilización exclusiva de CPU (Bujaruelo).........................................................47 Utilización exclusiva de GPUs (Bujaruelo).......................................................56 Utilización conjunta de CPU y GPU (Bujaruelo)...............................................64 Utilización exclusiva de cores ARM (K2H).......................................................65 Utilización conjunta de ARM y DSP (K2H)......................................................70 5 Conclusiones......................................................................................................73 Conclusions..........................................................................................................74 6 Bibliografía.......................................................................................................75 4 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Palabras clave •OmpSs •CUDA •OpenCL •Arquitecturas heterogéneas •Paralelismo a nivel de tareas •Consumo energético •Procesadores Gráficos (GPUs) •Procesadores Digitales de Señal (DSPs) 5 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Keywords •OmpSs •CUDA •OpenCL •Heterogeneous architectures •Task parallelism •Energy consumption •Graphics Processors (GPUs) •Digital Signal Processors (DSPs) 6 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Resumen El presente trabajo estudia la viabilidad a la hora de aplicar un modelo de programación basado en la extracción de paralelismo a nivel de tareas sobre distintas arquitecturas heterogéneas basadas en un procesador multinúcleo de propósito general acelerado con uno o más aceleradores hardware. Se ha implementado una aplicación completa cuyo objetivo es la detección de bordes en una imagen (implementando el Algoritmo de Canny), y se ha evaluado en detalle su rendimiento sobre distintos tipos de arquitecturas, incluyendo CPUs multinúcleo de última generación, sistemas multi-GPU y una arquitectura objetivo basada en procesadores ARM Cortex-A15 acelerados mediante un DSP C66x de la compañía Texas Instruments. Los resultados experimentales demuestran la viabilidad de este tipo de implementación también para arquitecturas heterogéneas novedosas como esta última, e ilustran la facilidad de programación que introduce este tipo de modelos de programación sobre arquitecturas de propósito específico. Abstract This work studies the possibility of applying programming models based on the extraction of task parallelism on different heterogeneous architectures based on multi-core processors accelerated with one or more hardware accelerators. We have implemented a complete application for edge detection (Canny Algorithm), and we have evaluated in detail the performance on different architectures, including novel multi-core CPUs, systems equipped with multiple GPUs and a target architecture based on ARM Cortex-A15 processors accelerated through a C66x DSP manufactured by Texas Instruments. The expermiental results validate the usage of the aforementioned programming models also for novel heterogeneous architectures, and illustrate the ease of programming introduced by this kind of programming models on specific-purpose architectures. 7 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores 8 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores 1 Introducción y objetivos En este capítulo se detalla la motivación principal del trabajo realizado, así como los principales objetivos planteados para su desarrollo. Durante los últimos años, las exigencias computacionales dictadas por los problemas surgidos en ciencia e ingeniería han aumentado la capacidad de cálculo de los procesadores, con el fin de realizar cálculos y simulaciones cada vez más complejas, minimizando el tiempo de respuesta. Tradicionalmente, la Ley de Moore [2], que determina el número de transistores que es posible integrar en una misma superficie de silicio, se ha complido hasta la fecha. Sin embargo, el incremento en frecuencia que posibilita, haciendo cada vez más rápidos los procesadores, se vio frenado en la pasada década, surgiendo como respuesta el concepto de procesadores multinúcleo. Este tipo de procesador replica la cantidad de unidades de procesamiento, haciendo posible que aquellos programas que puedan explotar este nivel de paralelismo vean aumentado su rendimiento generación tras generación. Sin embargo, el uso de procesadores multinúcleo también ha visto frenado su desarrollo en los últimos años, siendo su consumo energético una de las principales barreras de cara a su evolución. En respuesta al creciente consumo energético de las arquitecturas de altas prestaciones, en los últimos años ha surgido un gran interés por el uso de plataformas heterogéneas, con especial énfasis no sólo en el rendimiento, sino en la reducción del consumo energético y, por tanto, en la mejora de la eficiencia energética de las arquitecturas. El uso de arquitecturas heterogéneas es, por tanto, una tendencia creciente de cara a construir grandes supercomputadores que puedan responder a las demandas computacionales de la ciencia y la ingeniería. De entre este tipo de plataformas, destaca el uso de aceleradores hardware, que se adaptan de forma óptima a cierto tipo de aplicaciones, y aceleran el cómputo de ciertas partes de los algoritmos. Un ejemplo concreto es el uso de procesadores gráficos (GPUs), que en los últimos años ha emergido como un estándar a la hora de realizar implementaciones de alto rendimiento para cálculo de propósito general. Aún así, aunque más eficientes desde el punto de vista computacional y energético, el uso de aceleradores y procesadores multinúcleo cada vez más potentes (y por tanto, consumiendo mayores potencias), ha hecho resurgir la preocupación por la imposibilidad de construir grandes supercomputadores con un coste energético asumible. De hecho, se calcula que, de seguir la tendencia actual en la construcción de supercomputadores basados en aceleradores hardware, el coste energético asociado a cada centro de datos en pocos años será sencillamente inasumible. En respuesta a esto, se están estudiando nuevas tendencias a la hora de construir este tipo de plataformas que combinen, a la vez, gran eficiencia energética y prestaciones razonables. Una de las tendencias es el uso de procesadores y aceleradores de bajo coste y consumo, típicamente desarrollados para el mercado móvil, reutilizando y explotando sus características para un uso mucho más específico. Ejemplos de esta tendencia son los procesadores ARM, acelerados con procesadores gráficos de bajo consumo, u otro tipo de aceleradores como procesadores digitales de señal (DSPs). En la actualidad, existe gran interés en estudiar la viabibilidad de este tipo de 9 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Jerarquía de threads Las GPUs Nvidia constan de cientos (en algunos casos, miles) de unidades de cómputo, típicamente llamadas núcleos), cada uno dedicado a la ejecución de un flujo de ejecución independiente (hilo de ejecución). Por lo tanto, permiten, y explotan, un elevado grado de paralelismo de grano fino, siendo estas sus características: •Existe un súper grupo de bloques de threads, llamado grid. •Cada bloque de threads está formado por un conjunto de threads. •Cada bloque dentro de un grid se identifica de forma única mediante un identificador uni, bi o tridimensional, en función de las necesidades del problema. •Cada thread dentro de un bloque posee un identificador único, nuevamente uni, bi o tridimensional. De forma automática, cada thread instancia la variable threadIdx, cuyas componentes (x, y, z) identifican de forma unívoca al hilo dentro de su bloque. El identificador global del thread se puede obtener fácilmente a partir de dicha información, y del identificador del bloque al que pertenece; por ejemplo, trabajando en una dimensión, un identificador único para un determinado hilo puede obtenerse mediante expresiones sencillas de tipo: int threadX = threadIdx.x + blockDim.x * blockIdx.x; •Los threads de la GPU son ligeros, con poca o nula sobrecarga de planificación y presentan cambios de contexto rápidos; en cambio los threads de CPU son pesados, tiene sobrecarga de planificación y cambios de contexto mucho más lentos. •Dado un identificador global único, típicamente se utiliza dicha información para realizar un reparto de aquellos datos o bloques de datos sobre los que trabajará el hilo de ejecución. Desde este punto de vista, las GPUs siguen un paradigma SIMD (Simple Instruction Multiple Data): cada hilo de ejecución ejecuta exactamente la misma instrucción en un determinado punto de la ejecución, pero trabajando sobre distintos datos en función de su identificador. 16 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Espacios de memoria Las GPUs compatibles con CUDA presentan distintas zonas de memoria, cada una de ellas accesible a nivel de hilo, bloque o grid de ejecución: •Registros: Disponible y accesible únicamente por el thread al que está asociada, en modo lectura/escritura. •Memorial local: Disponible y accesible en modo lectura/escritura por todos los hilos que componen un mismo bloque de hilos. Su latencia y ancho de banda es similar a la de los registros, aunque su tamaño está limitado a pocos Kbytes por multiprocesador. •Memoria global: Para lectura/escritura desde cualquier bloque. Típicamente de gran tamaño, permite comunicar hilos pertenecientes a distintos bloques. •Memoria constante: Región de la memoria global, sólo para lectura y accesible desde cualquier bloque de hilos. •Memoria textura: Región de la memoria global, sólo para lectura y accesible desde cualquier bloque de hilos, cacheable y utilizable a través de APIs específicas. 17 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Ejemplo de código CUDA Se muestra a continuación un ejemplo sencillo de ejecución de un código CUDA. El programa se divide en dos partes: programa principal, ejecutado en CPU y kernel, ejecutado en GPU por tantos hilos como se deseen. El programa principal, mostrado a continuación, se divide en tres partes principales: 1. Reserva de un buffer de memoria de tamaño MEM_SIZE bytes, tanto en RAM como en memoria global, a través de funciones específicas en CPU y GPU. 2. Configuración de la ejecución (que en este caso incluye un bloque de hilos formado por un único hilo), e invocación del kernel utilizando sintaxis CUDA. 3. Copia de los datos inicializados en GPU de vuelta a memoria RAM, previa a la impresión por pantalla del contenido del buffer. Programa principal: Código GPU (kernel): 18 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores OpenCL OpenCL [1] son las siglas de Open Computing Language, lenguaje de computación abierto. Es el primer estándar de programación verdaderamente abierto. Permite crear aplicaciones que pueden ejecutarse tanto en unidades centrales de procesamiento como unidades de procesamiento gráfico. Para muchos es considerada la API que tiene mayor probabilidad de ejecutarse aplicaciones que funcionen usando las GPUs, al ser multiplataforma y no tener restricciones tanto de hardware como de sistema operativo. La principal diferencia con CUDA es que OpenCL puede ser ejecutado en cualquier dispositivo que implemente el estándar,siendo abierto no es únicamente Nvidia. Es decir, que OpenCL además de poder ejecutarse únicamente en GPUs, puede ser ejecutado en CPUs. OpenCL™ se desarrolló en un comité de estándares abiertos con representantes de los principales proveedores de la industria y les ofrece a los usuarios lo que han estado reclamando: una solución no de propiedad exclusiva, de varios proveedores, para acelerar las aplicaciones en las CPU, GPU y APU. AMD, un patrocinador inicial de OpenCL™ e innovador y proveedor líder de CPU, APU y GPU de alto rendimiento, está en una posición exclusiva en esta industria para ofrecer una plataforma de aceleración completa para OpenCL™. Características •Soporte del modelo de programación paralela a nivel de datos yde tareas •Emplea un subconjunto del lenguaje de programación C99 + extensiones para programación paralela eficaz y segura. •Permite la interacción eficiente con APIs gráficas como OpenGL, OpenGLES y DirectXentre otras. •Define requisitos numéricos basados en el estándar IEEE 754. 19 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Programación en OpenCL Todas las funciones de OpenCL se agrupan en los ficheros llamados kernels (al igual que en CUDA), siendo ficheros con la extensión “cl”, siendo el lenguaje basado en C. Conceptualmente, el modelo de programación es muy similar a CUDA. Todas las funciones OpenCl llevan el acrónimo __kernel para ser identificadas. Los punteros deben de llevar el acrónimo __global si apuntan a una zona de memoria global, o __local si son zonas de memoria local. Por ejemplo: __kernel void prueba(int __global *a) Los programas de OpenCL se compilan formando código objeto para la CPU, y para la GPU. El código objeto que se ejecuta en la CPU determina los kernels (cantidad mínima de código ejecutable) a ejecutar en cada una de las GPUs o dispositivos compatibles, compilándose éstos en tiempo de ejecución. Precisamente en tiempo de ejecución, OpenCL genera un contexto (Context) que se asocia a la unidad que se encargará de ejecutar el programa. Este contexto se encarga de manejar los programas, los kernels, los objetos de memoria y las colas de comandos, y está típicamente asociado a un dispositivo concreto. Jerarquía de threads A diferencia de CUDA, OpenCL permite programar en todo tipo de dispositivos, en concreto si tenemos una GPUs (pudiendo ser o no, Nvidia),constan de cientos de núcleos los cuales tiene el procesamiento de un hilo cada uno, por lo tanto permitiendo un grado de paralelismo bastante alto, siendo estas sus características: •Cada hebra es un work-item. •Cada work-item se ejecuta en paralelo en un núcleo siguiendo un paradigma SIMD. 20 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores •El conjunto de work-items de un mismo núcleo se llama work-group (es decir, equivalente al concepto de bloque de hilos en CUDA). •Cada work-group comparte memoria local y permite comunicar y sincronizar los workitems que lo componen. •Cada work-item posee un identificador único en la configuración global de ejecución (es decir, no es necesario computarlo explícitamente, como sí ocurría en CUDA). Gestión de memoria La división por work-group permite tener memoria privada para cada uno de ellos y a su vez memoria compartida con los demás, siendo estas sus características: •Memoria privada: Disponible y accesible únicamente por el work-item al que está asociada. •Memorial local: Para lectura y escritura, accesible desde un único work-group (variables compartidas por work-items dentro de un work-group). 21 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores •Memoria global: Para lectura/escritura desde cualquier work-item o work-group. Representa a la memoria de cada dispositivo. •Memoria constante: Región de la memoria global, sólo para lectura desde work-items. Es constante durante la ejecución del kernel, y puede ser leída y escrita por la aplicación host. •Memoria del Host: Memoria asociada a la CPU que actúa como host. Código de ejemplo OpenCL: programa principal. Nótese la complejidad del código OpenCL en su parte host equivalente al desarrollado en CUDA. Esta complejidad demuestra el compromiso entre portabilidad del código, que ahora es compatible con cualquier plataforma paralela con soporte OpenCL, y facilidad de programación. Aunque fuera del alcance del presente trabajo, cabe destacar la cantidad de invocaciones a la API de OpenCL desde el host para configurar contextos de ejecución, dispositivos, colas de comandos, creación de buffers, transferencias de datos y configuraciones de ejecución del kernel. 22 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores 23 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Código ejemplo OpenCL: Kernel Nótese la similitud del código de kernel desarrollado con respecto al equivalente CUDA (ver sección anterior): OmpSs Como se ha detallado en las anteriores secciones, tanto CUDA como OpenCL permiten programar, de forma relativamente sencilla, dispositivos aceleradores compatibles. Aunque su introducción como modelos de programación ha facilitado y popularizado en gran medida el uso de este tipo de hardware, todavía presentan problemas graves relacionados con la facilidad de programación: 1. Ambos modelos de programación requieren una intervención explícita por parte del programador a la hora de gestionar tanto la reserva y liberación de memoria, como la transferencia de datos entre espacios de memoria. 2. OpenCL requiere el uso de APIs complejas para la configuración previa a la ejecución, típicamente ocupando decenas de líneas. Esto suele conllevar errores de programación en muchos programas, independientemente de su sencillez. 3. El uso de varios aceleradores simultáneamente es complejo utilizando tanto OpenCL como CUDA. 4. La gestión eficiente de las transferencias de datos entre espacios de memoria, intentando reducir aquellas transferencias innecesarias resulta responsabilidad del programador, y por tanto suele ser específica para un problema en concreto, y típicamente subóptima. En respuesta a estas limitaciones han surgido nuevos modelos de programación, de entre los que destaca OmpSs. OmpSs [4] es un modelo de programación basado en la extracción y explotación de paralelismo a nivel de tareas desarrollado por el Barcelona Supercomputing Center (BSC). El 24 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores objetivo principal de OmpSs es facilitar la programación paralela de aplicaciones, delegando la ejecución paralela a un componente software (comúnmente denominado runtime o planificador de tareas) que, de forma automática, analiza las tareas anotadas por el usuario a través de #pragmas en el código, así como sus dependencias de datos, y las ejecuta de forma concurrente en los distintos procesadores disponibles sin ningún tipo de intervención por parte del usuario. Las ventajas de este tipo de paradigma son múltiples. En primer lugar, la labor del desarrollador se limita a identificar qué partes del código (por ejemplo, funciones) son candidatas a ser consideradas tareas, así como a indicar qué datos de entrada recibe la tarea y qué datos genera. A partir de es punto, el planificador de tareas decide, en tiempo de ejecución, cuándo ejecutar cada tarea (gestionando de forma automática las dependencias de datos) y sobre qué plataforma ejecutarla. En sistemas heterogéneos, además, OmpSs se encarga, de forma transparente, de gestionar las transferencias de datos entre espacios de memoria siempre que sea necesario. En general, pues, un código secuencial puede ser traducido a OmpSs sin un gran esfuerzo por parte del programador. Además, mediante los parámetros específicos del planificador, es posible modificar las políticas de uso de procesador, algoritmos de planificación o utilización concurrente de distintos tipos de arquitecturas, todo ello sin modificar el código. Conseguir una funcionalidad similar realizando una programación de menor nivel (por ejemplo, exclusivamente basada en CUDA) requeriría un esfuerzo mucho mayor. Modelo de programación Como se ha descrito, el modelo de programación de OmpSs se basa en añadir pequeñas anotaciones en forma de #pragmas al código secuencial, de modo que se informa al planificador de tareas de la existencia de una tarea. Una tarea es la unidad mínima de planificación sobre cada tipo de procesador disponible en el sistema (por ejemplo, un núcleo o una GPU). De hecho, ya que dichos pragmas son ignorados por el compilador sin soporte para OmpSs, cualquier código anotado con pragmas puede funcionar de forma secuencial sin ninguna modificación, y viceversa. OmpSs se basa en dos componentes fundamentales: 1. Compilador (Mercurium). Se trata de un compilador específico fuente-a-fuente (es decir, transforma el código del usuario en un código con similares características, pero ampliado para dar soporte al modelo de programación). Básicamente, inserta invocaciones a rutinas implementadas en el planificador (por ejemplo, para añadir nuevas tareas a la cola de tareas, realizar sincronizaciones, etc.) 2. Planificador (Nanox). Se trata de un software sofisticado que, enlazado con nuestro programa y a través de las invocaciones a su API introducidas por Mercurium, es capaz de planificar de forma dinámica y en tiempo de ejecución las tareas anotadas por el usuario. A continuación se muestran los pragmas principales soportados por OmpSs, así como una breve descripción del funcionamiento del planificador de tareas. 25 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Por contra, la principal desventaja es la necesidad de adaptar los algoritmos al paradigma de paralelismo a nivel de tareas (cosa no siempre posible), y el menor control sobre la ejecución, ya que ésta es gestionada automáticamente por el planificador, con mínima intervención para el usuario. Ejemplos de códigos OmpSs Ompss + CUDA Ompss + OpenCl 32 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores 3 Problema objetivo. Detección de bordes En este capítulo se explica el algoritmo de Canny (etapa por etapa) y la motivación de realizarlo. Descripción del algoritmo y motivación El algoritmo usado para nuestras pruebas es Canny. El programa ha sido creado totalmente desde 0, en código C, y usando las herramientas atrás descritas. El algoritmo de Canny fue desarrollado por John F.Canny en 1986, donde se utiliza varias etapas para detectar la mayoría de bordes en una imagen dada. El propósito de este algoritmo es el de descubrir bordes en las imágenes que se llegaran a analizar con este algoritmo. Con la técnica de reducción significativa de datos en una imagen, preservando las propiedades estructurales de la imagen. Este algoritmo esta enfocado en los siguientes puntos: •Buena detección: El algoritmo debe marcar el mayor número real en los bordes de la imagen como sea posible. •Buena localización: Los bordes de marca deben estar lo más cerca posible del borde de la imagen real. •Respuesta mínima: El borde de una imagen sólo debe ser marcado una vez, y siempre que sea posible, el ruido de la imagen no debe crear falsos bordes Desde el punto de vista del modelo de progrmación OmpSs, este algoritmo resulta interesante, puesto que: 1. Presenta distintos tipos de tareas asociadas a cada etapa del algoritmo. 2. Presenta dependencias de datos entre tareas no triviales. 3. Las implementaciones de cada tarea son (relativamente) sencillas de implementar. 4. Cada tarea es altamente paralela a nivel de datos, por lo que permite una correcta explotación de los aceleradores hardware. Etapas •Suavizar: Desenfoque de la imagen para eliminar el ruido. •Encontrar gradientes: los bordes deben estar marcados en los gradientes de la imagen que tiene magnitudes grandes. 33 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores •No supresión máxima: Sólo los máximos locales se debe marcar como bordes. •Umbralización doble: Los posibles bordes deben estar determinados por umbralización. •Seguimiento por histéresis: Los bordes finales se determinan mediante la supresión de todas las aristas que no están conectados a una muy determinada borde (fuerte). Procesamiento por bloques. Descripción de las tareas. Para fomentar el paralelismo, la imagen es divida en bloques totalmente independientes entre si en una misma etapa, de tal forma que dos trozos de imagen distintas se puedan realizar simultáneamente. El tamaño de bloque es dinámico, es decir, el programa es capaz de descomponer la imagen en cualquier tamaño de bloque dado por el usuario. Cada bloque de la imagen es procesado por cada función, siendo a su vez una tarea. Algunas etapas como por ejemplo la primera, dado un pixel, utiliza los de su alrededor y lo multiplica por una matriz dada para obtener el valor resultado de ese pixel. Este paso puede dar error en los pixeles cercanos a los limites de la imagenes, para ello hemos agrandado la imagen rellenado de ceros todos los limites de la imagen. Es decir, si la imagen es de 15x15, la hemos agrandado a 16x16 con ceros. Etapas Se muestran a continuación las etapas o fases principales que componen el procesamiento de la imagen. En nuestro caso, existe una primera fase de preprocesado en la que la imagen a color es transformada en una imagen en escala de grises, cuyos detalles de implementación se obvian en la siguiente descripción: 34 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores La siguiente fase consiste en limpiar la imagen de ruidos. Es inevitable que todas las imágenes tomadas desde una cámara contengan cierta cantidad de ruido; el objetivo de esta etapa es evitar que el ruido introducido se confunda con bordes. Para ello se le aplica un filtro de Gauss (en nuestro caso, un filtro de Gauss con una desviación estándar de σ = 1,4). El filtro de Gauss variar en tamaño; en nuestro caso de estudio se ha elegido un filtro de tamaño 5x5: La siguiente figura muestra un ejemplo de aplicación del anterior filtro sobre una imagen de entrada en escala de grises: A continuación, el algoritmo de Canny encuentra básicamente bordes, considerando éstos como aquellas zonas de la imagen con variación más rápida de la intensidad. Estas áreas se encuentran mediante la determinación de los gradientes de la imagen. Los gradientes en cada píxel en la imagen suavizada se determinan mediante la aplicación de lo que se conoce como filtro de Sobel: el primer paso es aproximar el gradiente en las direcciones x e y respectivamente, aplicando el operador de Sobel: 35 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Las magnitudes del gradiente (también conocidos como los puntos fuertes de borde) se pueden determinar calculando la arcotangente entre los puntos resultados de x e y: La siguiente imagen muestra el resultado de la aplicación del filtro de Sobel sobre la imagen de entrada resultante de la fase anterior: Por último, se aplica una fase llamada de de no supresión máxima. El propósito de este paso es convertir los "enmascarados" bordes en la imagen de las magnitudes del gradiente a los "fuertes" bordes. Básicamente esto se hace mediante la preservación de todos los máximos locales en la imagen de gradiente, y la eliminacion del resto de información. El algoritmo para cada píxel de la imagen de gradiente es el siguiente: 1. Alrededor de la dirección del gradiente theta más cercano a 45º. 2. Comparar la resistencia del borde del píxel actual con la resistencia de los bordes en dirección al píxel en el gradiente positivo y negativo. Es decir, si la dirección del gradiente 36 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores es el norte (theta= 90º), por ejemplo, comparar con los píxeles hacia el norte y el sur. 3. Si la resistencia del borde del píxel actual es el más grande; preservar el valor de la resistencia de los bordes. Si no es así, suprimir (es decir, eliminar) el valor. La siguiente figura muestra un ejemplo de aplicación de dicha fase sobre el resultado de la aplicación del operador Sobel: Implementación utilizando OmpSs En este capítulo se introduce de forma de más precisa el desarrollo del programa desarrollado y su adaptación a un paradigma de paralelismo a nivel de tareas, explicando en detalle el tratamiento de la imagen desde su origen hasta su destino. 37 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Visión general de la implementación El programa parte de una imagen en colores, y tiene como objetivo detectar los bordes de la misma. Como se ha detallado anteriormente, la imagen es previamente transformada a escala de grises, para a continuación aplicar las distintas fases del algoritmo de Canny de forma secuencial. En nuestro caso y como se explicó anteriormente, las dos últimas fases se omitirán en la descripción, al no ser estrictamente necesarias (a partir de la tercera fase, no_máximos, la detección de bordes ha sido realizada, siendo las restantes fases de mejora de calidad de los bordes). Para dar una visión general del funcionamiento de la implementación, imaginemos que cada fase es una caja negra, la cual esta conectada a la siguiente fase, y que cada etapa no puede continuar si la etapa anterior no ha terminado. Cada etapa recibe un buffer de entrada y devuelve un buffer de salida, que contiene el resultado de la aplicación del tratamiento correspondiente. Más concretamente: •Primera fase (filtro gaussiano): ◦Recibe como buffer de entrada, la imagen en escala de blanco y negro. ◦La imagen es tratada y devuelta en un buffer de salida, el cual llamaremos buffer_gaussiano. •Segunda fase (filtro Sobel): ◦Recibe como buffer de entrada, la imagen tratada por la fase gaussiana (buffer_gaussiano). ◦La imagen es tratada y devuelta en un buffer de salida, el cual llamaremos buffer_sobel. •Tercera fase (supresión de máximos): ◦Recibe como buffer de entrada, la imagen tratada por la fase sobel (buffer_sobel). 38 Bermúdez Blanco, Javier Gauss Sobel Nomax Blanco y negro Imagen resultado
Trabajo Fin de Grado en Ingeniería de Computadores ◦La imagen es tratada y devuelta en un buffer de salida, el cual llamaremos buffer_no_max. •Por último, se escribe la imagen resultante a disco. Paralelismo a nivel de tareas y dependencias de datos Como se ha visto, cada fase requiere datos de la fase anterior. Más concretamente, en una implementación no orientada a bloques, cada fase requiere que la etapa anterior haya finalizado completamente (sobre toda la imagen) para proceder. Este proceso ralentiza el procesado de la imagen en sistemas con múltiples procesadores, al ser necesario esperar a que la imagen sea tratada completamente por la fase anterior. Para optimizar el trabajo, se ha optado por un procesamiento orientado a bloques: la imagen se divide en bloques de filas de tamaño configurable, que se identificarán como tareas a través de los mecanismos proporcionados por OmpSs y se asignarán, en tiempo de ejecución, a las distintas unidades de proceso existentes en el sistema. Este grado de paralelismo permite ejecutar, de forma concurrente, varias tareas asociadas a una misma fase, e incluso pertenecientes a fases distintas, siempre que las dependencias de datos hayan sido satisfechas. Además, este esquema permite ejecutar cada tarea en distintos núcleos del procesador y en los aceleradores disponibles. Por ejemplo, si disponemos de seis tareas listas para ser ejecutadas, podría pontencialmente lanzarse cuatro de ellas a núcleos de CPU, y dos restantes a GPUs disponibles (o a cualquier otro tipo de acelerador). Toda tarea que es lanzada a un núcleo de CPU procesa un bloque de la imagen de manera secuencial, pixel a pixel; en cambio las GPUs (u otros aceleradore) poseen múltiples núcleos, siendo aprovechables para aumentar el nivel de paralelismo. Para aprovechar este hecho, cada tarea que es lanzada a la GPU, es procesada en paralelo, es decir, cada pixel del bloque de la imagen es procesado por un núcleo de la GPU, aprovechando ya no solo el paralelismo a nivel de tareas (mediante OmpSs), sino también de datos (mediante CUDA u OpenCL). Desde este punto de vista, los aceleradores son vistos por OmpSs como “cajas negras”, o unidades mínimas de asignación de tareas; es el código interno de cada tarea quien extraerá paralelismo a nivel de datos de forma interna. Algunas de las fases descritas requieren, para calcular el valor de un pixel, los valores de los pixeles cercanos a él calculados en la fase anterior. Esto puede suponer un problema al calcular los píxeles en los extremos de la imagen, ya que pueden ser necesarios valores de pixeles que estén fuera del rango de la imagen. La solución que se ha implementado crea un halo (también conocido como padding o relleno) alrededor de la imagen o borde correspondiente. Al aplicar esta técnica surgen dependencias entre tareas mayores. Como se dijo anteriormente cada bloque depende de algunos bloques anteriores. Al introducir padding, cada bloque va a ser mayor, abarcando valores de otros bloques de su misma fase. Veamos un ejemplo: la aplicación del filtro de 39 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Sobel sobre el primer bloque de la imagen requerirá no sólo los datos de entrada correspondientes a dicho bloque (y obtenidos tras la aplicación del filtro gaussiano sobre el primer bloque de la imagen), sino también ciertas filas adicionales computadas tras aplicar el filtro gaussiano al segundo bloque de la imagen. Por tanto, existe una dependencia de datos entre la tarea que aplicará el filtro Sobel al primer bloque de la imagen, y las dos primeras tareas que aplicarán el filtro gaussiano sobre los dos primeros bloques de la imagen. A continuación se muestra un ejemplo de dependencias entre bloques, para una imagen de 1024x1024, en bloques de 128, es decir, 8 bloques por fase: La gestión de este tipo de dependencias de datos es realmente compleja en el caso de ser realizada a mano, e ilustra las ventajas de utilizar un modelo de programación como OmpSs, como se verá a continuación. Esquema algorítmico y detalles de implementación En una implementación secuencial, el esquema algorítmico básico procesaría cada una de las tres fases de forma consecutiva: la finalización del procesado de una de ellas supondría el inicio de la siguiente. En un procesado por bloques, la aplicación de cada fase se realiza a nivel de bloque de filas, también de forma secuencial, como muestra el siguiente código. 40 Bermúdez Blanco, Javier Gaussiano Sobel No_máximos Blanco y negro Imagen resultado
Trabajo Fin de Grado en Ingeniería de Computadores Cabe destacar dos observaciones principales: 1. En este caso, no existe ningún tipo de paralelismo a nivel de tareas; es decir, cada tarea se ejecuta de forma exclusivamente secuencial, sin solapar su procesamiento con ninguna otra. 2. Las invocaciones a cada función de procesamiento de la imagen se ejecutan exclusivamente sobre CPU, sin utilizar en ningún caso ninguno de los posibles aceleradores disponibles. Una migración de este código para ser acelerado mediante uno o varios aceleradores, requeriría una reescritura completa del código. Sin embargo, mediante el uso de OmpSs, es posible realizar una transformación del mismo con mínimos cambios. De hecho, los cambios principales serían básicamente dos: 1. Desarrollo de kernels específicos (CUDA u OpenCL) para la implementación de cada fase en el acelerador. 2. Etiquetado de cada tarea mediante pragmas, indicando sus datos de entrada y salida, y la plataforma o plataformas en las que debe ejecutarse. Este último caso es de especial interés para el desarrollo del trabajo propuesto. A continuación, se detalla el mecanismo de anotación para una de las tareas (gaussiano) utilizando #pragmas OmpSs. Nótese como, si dicho pragma es eliminado o no soportado por el compilador, el programa seguiría funcionando de forma correcta (aunque secuencial): 41 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 256x256, utilizando entre 1 y 56 hilos de ejecución. 48 Bermúdez Blanco, Javier 0 20 40 60 80 100 120 140 0 1000 2000 3000 4000 5000 6000 7000 8000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos) 0 20 40 60 80 100 120 140 0 500 1000 1500 2000 2500 3000 3500 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundo 0 20 40 60 80 100 120 140 0 0,5 1 1,5 2 2,5 3 3,5 Consumo 1 2 4 8 16 28 32 56 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 512x512, utilizando entre 1 y 56 hilos de ejecución. 49 Bermúdez Blanco, Javier 0 50 100 150 200 250 300 0 5000 10000 15000 20000 25000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos) 0 50 100 150 200 250 300 0 1000 2000 3000 4000 5000 6000 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundos 0 50 100 150 200 250 300 0 2 4 6 8 10 12 Consumo 1 2 4 8 16 28 32 56 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 768x768, utilizando entre 1 y 56 hilos de ejecución. 50 Bermúdez Blanco, Javier 16 32 64 128 256 0 5000 10000 15000 20000 25000 30000 35000 40000 45000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos) 16 32 64 128 256 0 2000 4000 6000 8000 10000 12000 14000 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundos 16 32 64 128 256 0 5 10 15 20 25 Consumo 1 2 4 8 16 28 32 56 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 1024x1024, utilizando entre 1 y 56 hilos de ejecución. 51 Bermúdez Blanco, Javier 0 100 200 300 400 500 600 0 10000 20000 30000 40000 50000 60000 70000 80000 90000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos) 0 100 200 300 400 500 600 0 1000 2000 3000 4000 5000 6000 7000 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundo 0 100 200 300 400 500 600 0 5 10 15 20 25 30 35 40 45 Consumo 1 2 4 8 16 28 32 56 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 2048x2048, utilizando entre 1 y 56 hilos de ejecución. 52 Bermúdez Blanco, Javier 0 200 400 600 800 1000 1200 0 50000 100000 150000 200000 250000 300000 350000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos) 0 200 400 600 800 1000 1200 0 1000 2000 3000 4000 5000 6000 7000 8000 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundo 0 200 400 600 800 1000 1200 0 20 40 60 80 100 120 140 160 Consumo 1 2 4 8 16 28 32 56 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 4096x4096, utilizando entre 1 y 56 hilos de ejecución. 53 Bermúdez Blanco, Javier 0 500 1000 1500 2000 2500 0 200000 400000 600000 800000 1000000 1200000 1400000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos) 0 500 1000 1500 2000 2500 0 1000 2000 3000 4000 5000 6000 7000 8000 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundo 0 500 1000 1500 2000 2500 0 100 200 300 400 500 600 700 Consumo 1 2 4 8 16 28 32 56 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 8192x8192, utilizando entre 1 y 56 hilos de ejecución. 54 Bermúdez Blanco, Javier 0 500 1000 1500 2000 2500 3000 3500 4000 4500 0 1000 2000 3000 4000 5000 6000 7000 8000 Rendimiento 1 2 4 8 16 28 32 56 Bloque MegaBits/Segundo 0 500 1000 1500 2000 2500 3000 3500 4000 4500 0 500 1000 1500 2000 2500 Consumo 1 2 4 8 16 28 32 56 Bloque Julios 0 500 1000 1500 2000 2500 3000 3500 4000 4500 0 1000000 2000000 3000000 4000000 5000000 6000000 Tiempo 1 2 4 8 16 28 32 56 Bloque Tiempo(microsegundos)
Trabajo Fin de Grado en Ingeniería de Computadores Discusión de resultados Al analizar una series de distintos tamaños para una misma imagen sobre esta arquitectura, podemos apreciar una serie de tendencias y observaciones generales: •Tiempo de ejecución 1. El uso de la tecnología Hyperthreading implica que sólo ante el uso de tantos threads como cores físicos hay disponibles en el sistema se alcance un rendimiento óptimo. Aumentar el número de hilos de ejecución por encima de dicho límite degrada el rendimiento para todos los tamaños de problema. 2. En general, el rendimiento aumenta sustancialmente a medida que el tamaño de imagen aumenta. Típicamente, el procesamiento de imágenes de mayor tamaño implica la posibilidad de utilizar tamaños de bloque mayores, con mejor aprovechamiento de la jerarquía de memoria. •Tamaños de bloque 1. Al igual que en el apartado anterior, sólo ante un número reducido de bloques tiene sentido utilizar un número reducido de hebras. En general, como es lógico, sólo cuando el número de bloques es considerable resulta beneficioso aumentar el número de hebras de ejecución. 55 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Utilización exclusiva de GPUs (Bujaruelo) Por limitaciones en la cantidad de memoria disponible en GPU, sólo ha sido posible testear imágenes de hasta un tamaño de 4096x4096. •Tamaño de imagen 128x128, utilizando 1, 2 y 3 GPUs. 56 Bermúdez Blanco, Javier 16 32 64 0 500 1000 1500 2000 2500 3000 Rendimiento 1 2 3 Bloque MegaBits/Segundos 16 32 64 0 5000 10000 15000 20000 25000 30000 35000 Tiempo 1 2 3 Bloque Tiempo(microsegundos) 16 32 64 0 2 4 6 8 10 12 14 16 18 Consumo 1 2 3 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 256x256, utilizando 1, 2 y 3 GPUs. 57 Bermúdez Blanco, Javier 16 32 64 128 0 10000 20000 30000 40000 50000 60000 70000 80000 90000 Tiempo 1 2 3 Bloque Tiempo(microsegundos) 16 32 64 128 0 1000 2000 3000 4000 5000 6000 7000 Rendimiento 1 2 3 Bloque MegaBits/Segundos 16 32 64 128 0 5 10 15 20 25 30 35 40 45 Consumo 1 2 3 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores Utilización conjunta de CPU y GPU (Bujaruelo) Apreciaciones generales y observaciones Al combinar CPU y GPU, se puede apreciar un mayor aprovechamiento de los recursos, bajando los tiempos de ejecución, aumentando significativamente el rendimiento y reduciéndose el consumo. 64 Bermúdez Blanco, Javier 128 256 512 768 1024 2048 4092 0 200000 400000 600000 800000 1000000 1200000 1400000 Tiempo 16 32 64 128 256 512 1024 2048 Matriz Tiempo(microsegundos) 128 256 512 768 1024 2048 4092 0 2000 4000 6000 8000 10000 12000 14000 16000 Rendimiento 16 32 64 128 256 512 1024 2048 Matriz MegaBits/Segundos 128 256 512 768 1024 2048 4092 0 100 200 300 400 500 600 700 Consumo 16 32 64 128 256 512 1024 2048 Matriz Julios
Trabajo Fin de Grado en Ingeniería de Computadores Utilización exclusiva de cores ARM (K2H) En este caso, la limitación de la máquina no permite testear con imágenes mayores de 768x768. •Tamaño de imagen 128x128, utilizando 1, 2 y 4 cores. 65 Bermúdez Blanco, Javier 16 32 64 0 5000 10000 15000 20000 25000 30000 35000 Tiempo 1 2 4 Bloque Tiempo(microsegundos) 16 32 64 0 500 1000 1500 2000 2500 3000 Rendimiento 1 2 4 Bloque MegaBits/Segundos 16 32 64 0 0,05 0,1 0,15 0,2 0,25 0,3 0,35 Consumo 1 2 4 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 256x256, utilizando 1, 2 y 4 cores. 66 Bermúdez Blanco, Javier 16 32 64 128 0 20000 40000 60000 80000 100000 120000 Tiempo 1 2 4 Bloque Tiempo(microsegundos) 16 32 64 128 0 100 200 300 400 500 600 700 800 Rendimiento 1 2 4 Bloque MegaBits/Segundos 16 32 64 128 0 0,2 0,4 0,6 0,8 1 1,2 Consumo 1 2 4 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 512x512, utilizando 1, 2 y 4 cores. 67 Bermúdez Blanco, Javier 16 32 64 128 256 0 50000 100000 150000 200000 250000 300000 350000 400000 450000 Tiempo 1 2 4 Bloque Tiempo(microsegundos) 16 32 64 128 256 0 50 100 150 200 Rendimiento 1 2 4 Bloque MegaBits/Segundos 16 32 64 128 256 0 0,5 1 1,5 2 2,5 3 3,5 4 4,5 Consumo 1 2 4 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores •Tamaño de imagen 768x768, utilizando 1, 2 y 4 cores. 68 Bermúdez Blanco, Javier 16 32 64 128 256 0 100000 200000 300000 400000 500000 600000 700000 800000 Tiempo 1 2 4 Bloque Tiempo(microsegundos) 16 32 64 128 256 0 20 40 60 80 100 Rendimiento 1 2 4 Bloque MegaBits/Segundos 16 32 64 128 256 0 1 2 3 4 5 6 7 8 Consumo 1 2 4 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores Apreciaciones Al analizar las series de experimentos para distintos tamaños para una misma imagen, podemos apreciar una serie de tendencias: •Tiempo de ejecución. No existe en este caso una gran diferencia entre la elección de uno, dos o cuatro threads; se puede apreciar una pequeña mejora con dos threads, pero no al aumentar hasta cuatro threads. La razón estriba en lo costoso del proceso de planificación para tamaños de bloque de reducidas dimensiones con respecto al breve tiempo de procesamiento. •Rendimiento/consumo. Comparativamente, el rendimiento es mucho menor que en una máquina de alto rendimiento. Sin embargo, el consumo energético es mucho menor (y por tanto la eficiencia energética mayor). Este es uno de los principales puntos fuertes de este tipo de arquitecturas. 69 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores Utilización conjunta de ARM y DSP (K2H) Apreciaciones En general, los tiempos de ejecución no son significativamente mejores al introducir el uso del DSP 70 Bermúdez Blanco, Javier 128 256 512 768 0 100000 200000 300000 400000 500000 600000 700000 800000 Tiempo 16 32 64 128 256 Bloque Tiempo(microsegundos) 128 256 512145,25 768 0 500 1000 1500 2000 2500 3000 Rendimiento 16 32 64 128 256 Bloque MegaBits/Segundos 128 256 512 768 0 1 2 3 4 5 6 7 8 Consumo 16 32 64 128 256 Bloque Julios
Trabajo Fin de Grado en Ingeniería de Computadores como coprocesador. Sin embargo, la limitación en la cantidad de memoria disponible hace que tanto el tamaño de las imágenes como el tamaño de bloque se vea seriamente limitado, y por tanto el rendimiento obtenido no sea significativamente mejor. Se espera, no obstante, que dicho rendimiento mejore para imágenes de mayor tamaño (siempre que el tamaño de memoria deje de ser una limitación en futuras generaciones). 71 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores 72 Bermúdez Blanco, Javier
Trabajo Fin de Grado en Ingeniería de Computadores 5 Conclusiones En este trabajo se ha desarrollado una aplicación completa para la detección de bordes en imágenes en color explotando paralelismo a nivel de tareas. Aunque el objetivo general planteado consistió en realizar una implementación portable de dicho código sobre una arquitectura de proposito específico formada por procesadores ARM y acelerada mediante DSP, el desarrollo del proyecto ha permitido comprobar el funcionamiento y portabilidad de la misma, con ningún cambio en el código, sobre otro tipo de arquitecturas. Éstas incluyen procesadores de alto rendimiento y múltiples GPUs en un mismo sistema. Los principales hitos conseguidos se resumen en: 1. Se ha diseñado, implementado y evaluado el rendimiento de una implementación del algoritmo de Canny explotando paralelismo a nivel de tareas. 2. Internamente, se han desarrollado kernels utilizando los paradigmas CUDA y OpenCL para explotar el paralelismo interno de cada tipo de acelerador. 3. Se ha portado y evaluado, sin cambios en el código, dicha implementación a arquitecturas radicalmente distintas, basadas en GPUs y DSPs. 4. Se ha utilizado el mecanismo proporcionado por OmpSs para la ejecución concurrente de tareas en CPU y acelerador de forma transparente. Los resultados obtenidos demuestran la posibilidad de explotar los DSPs como plataforma de aceleración de código, y la posibilidad de utilizar un sistema basado en planificador de tareas (OmpSs) sobre este tipo de plataformas. Aunque los resultados obtenidos no son alentadores en términos de rendimiento, muchos de ellos se basan en las limitaciones actuales en cuanto a cantidad de memoria de los DSPs de nueva generación. La evaluación de los mismos códigos sobre plataformas similares futuras resultará trivial, puesto que no será necesario reimplementar los códigos. Cabe destacar que se trata de la primera experimentación sobre este tipo de plataformas utilizando OmpSs encontrada en la literatura. Como trabajo futuro, se propone la evaluación de otro tipo de implementaciones que exhiban paralelismo a nivel de tareas, la utilización de entornos precisos de medición de consumo energético y la optimización del código interno de las tareas para acelerar el tiempo de ejecución individual de cada una de ellas. 73 Bermúdez Blanco, Javier