El desarrollo de CUDAlicious fue orientado a prototipos incrementales [Sección 2.3], a continuación se describe la secuencia de dichos prototipos, sus entregables y resultados.
En la descripción de cada prototipo se hace especial énfasis en el objetivo específico del mismo, explicando también los problemas, conclusiones y la importancia de dicho prototipo. En la sección de la versión final [Sección III,5] se explica la integración final de todos los prototipos, y se centra en mostrar un resumen general del funcionamiento de CUDAlicious.
Primer Prototipo
El primer prototipo se planteó como una prueba critica para la integración total del modelo: ya se había desarrollado código en CUDA (archivos .cu), pero era necesario revisar en un prototipo dos factores importantes:
-
Integración de código CUDA con código C++
-
Proceso de compilación y de enlace para crear un ejecutable
La integración de CUDA con código C++ era necesaria debido a la concepción inicial del modelo, en donde se integraría varias librerías (OpenCV, ITK). De esta manera, los algoritmos programados en CUDA se utilizan como un componente más del modelo, mas no son el centro del mismo.
Como entregable de este prototipo se definió un tutorial de compilación y enlace de archivos .cu con archivos .cpp para crear un ejecutable. Este tutorial es la base para la integración con el framework de CREATIS, ya que evidencia la manera en la cual se debe configurar CMAKE para integrar CUDAlicious con el mismo.
Segundo Prototipo
En el segundo prototipo se decidió cargar imágenes 2D, utilizando OpenCV para su posterior filtrado.
-
Como primer paso, el archivo de imagen se debía cargar en memoria principal. Los archivos soportados por OpenCV se encuentran aquí [14].
La carga de la imagen se realizó en formato RGBA, esto genera un AoS de uchar4 que contiene los tres canales de color más el canal de transparencia de la imagen. Se decidió utilizar RGBA en vez de RGB para asegurar la mayor compatibilidad posible con imágenes 2D existentes. Un esquema del AoS generado por OpenCV se puede apreciar en la ilustración 10.
Ilustración 10: Array of Structures de la imagen 2D, foto sacada de [15]
COPIA DE IMAGEN A MEMORIA GPU
La imagen luego debe ser copiada a la memoria de la GPU para ejecutar el filtrado. En este prototipo no se tuvo en cuenta imágenes que excedieran el tamaño de la memoria de la GPU.
Como se explica en [9], Nvidia recomienda utilizar SoA en vez de AoS para procesar colecciones de datos que tengan varias propiedades (estructuras, clases, etc), ya que esto conlleva un aumento de rendimiento importante. Por ello, luego de copiar la imagen RGBA a memoria de GPU se realiza un paso intermedio que es la separación de canales.
Esta separación se lleva a cabo utilizando los recursos computacionales de la GPU, un esquema de cómo queda la memoria organizada en un SoA se muestra en la ilustración 11; la imagen original RGBA es borrada luego de la separación de canales.
Ilustración 11: Structure of Arrays de la imagen 2D, foto sacada de [15]
El diagrama 1 representa el proceso, diferenciando claramente que se ejecuta en la CPU y que en la GPU.
Diagrama 1: Carga de imagen con OpenCV y separación de canales en GPU
-
En este prototipo se diseñó el proceso de filtrado para imágenes en dos dimensiones, que utiliza el algoritmo [Sección 1.4.2.3, Marco Conceptual], siendo este proporcionado por Nvidia junto al SDK 5.5 de CUDA.
Debido al algoritmo usado, se requiere varios pasos para llegar a una imagen completamente filtrada, adicional a esto también es necesario disponer de un espacio extra que actúe como buffer para realizar el filtrado. El tamaño de dicho buffer no debería ser mayor al de la imagen original.
Ilustración 12: Proceso convolución separable
En la ilustración 12 se muestra el proceso seguido para la convolución separable. En el caso de imágenes cargadas con OpenCV, este proceso se realiza para cada uno de los diferentes canales de colores. Sin embargo, solo se utiliza un buffer, que se comparte entre todos los canales, ya que la secuencia de filtrado de los canales es serial.
Desde este prototipo, se decidió seguir utilizando siempre solo un buffer por dimensión, sin importar la cantidad de dimensiones que posea la imagen; Si se siguiera filtrando una vez más la imagen de la ilustración 12 bastaría con volver a utilizar el mismo buffer que ya se tenía en el primer paso, y si se requiriera volver a filtrar una vez mas el buffer sería el arreglo original de la dimensión.
El diagrama 2 representa el proceso ahora involucrando el filtrado por cada uno de los canales.
Diagrama 2: Convolución separable en cada uno de los canales en GPU
Cuarto Prototipo
En el cuarto prototipo se integró finalmente ITK para la lectura y escritura de imágenes de más de 2 dimensiones, se utilizan las factorías descritas en [16][17]. Las imágenes leídas vienen en dos archivos: uno tiene la extensión .mhd y define todos los metadatos de la imagen, el otro tiene la extensión .raw y tiene todos los datos de la imagen como tal.
Ilustración 13: Modelo de carga de imágenes
Dostları ilə paylaş: |