Procesamiento Paralelo
OpenCL - IntroducciónJavier Iparraguirre
Universidad Tecnológica Nacional, Facultad Regional Bahía Blanca 11 de Abril 461, Bahía Blanca, Argentina
http://www.frbb.utn.edu.ar/hpc/
¿Qué es OpenCL?
• OpenCL (Open Computing Language)
• Estándar para programar plataformas heterogéneas: CPUs, GPUs, DSPs,
• Consta de un lenguaje basado en C99 para armar y una especificación de la API
• Es soportado por la mayoría (sino todos) en la industria: AMD, Intel, NVIDIA, Apple, Android
Características
• Código portable :)
• Se define en cuatro partes
• Modelo de la plataforma
• Modelo de ejecución
• Modelo de memoria
Modelo de la plataforma
• Una unidad central (host) y varios dispositivos de procesamiento
• Los dispositivos de de procesamiento se dividen en unidades de procesamiento
• Las unidades de procesamiento se dividen en uno o varios elementos de procesamiento
• Cada elemento de procesamiento tiene su contador de programa!!!
Quien es quien
• Los host son procesadores de propósito general: x86 o similares
• Los dispositivos son los procesadores especializados: GPUs o DSPs
• Dependiendo de las implementaciones, los cores pueden ser considerados de diversas formas
Seleccionando una plataforma
• Usualmente, esta función se llama dos veces
• La primera llamada se usa para ver la cantidad de plataformas disponibles en la implementación
• Luego hacemos espacio para los objetos de la plataforma
• En la segunda llamada obtenemos los objetos de la plataforma
Seleccionando un dispositivo
• Una vez que seleccionamos la plataforma, consultamos por los dispositivos
• Se puede especificar el tipo de dispositivo que estamos buscando: todos, solo GPUs, solo CPUs
• Usualmente usamos dos veces a esta función como el caso anterior
Contexto
• Un contexto es un espacio para manejar los objetos y recursos de OpenCL
• En un programa OpenCL los siguientes conceptos están asociados a un contexto:
• Dispositivos
• Objetos de programa: implementación de los objetos de
cómputo
• Kernels: Funciones que corren en dispositivos OpenCL (el
código de los threads)
• Objetos de memoria: los datos operados en el dispositivo
(los datos de los threads)
• Colas de comandos: mecanismos de interacción de
dispositivos (transferencia de datos, ejecución de kernels y sincronización)
Contexto
• Cuando se crea un contexto, el programador provee una lista de dispositivos a asociar
Creando un contexto
• Con esta función creamos un contexto, se debe pasar la lista de dispositivos
• cl_context_properties especifica que plataforma a usar (NULL indica que se usa el provisto por el vendedor por defecto)
• Se provee un mecanismo de callback para reportar errores al usuario
Colas de comandos
• La cola de comandos es el mecanismo por el cual el procesador central (host) le pide una acción a un dispositivo (device)
• Hay transferencia de datos a la memoria y ejecución de tarea
• Cada dispositivo tiene su cola de comandos
• Los comandos pueden ser sincrónicos o asincrónicos
• Los comandos se pueden ejecutar en orden o no (out-of-order)
Creando un cola de comandos
• Hay relacion entre la cola de comandos y el contexto
• En las propiedades se especifica la ejecución fuera de orden y el sensado de desempeño (profilling)
Colas de comandos dentro del contexto
• Las colas de comandos asocian a los dispositivos con el contexto
Contexto
cola comandos
Objetos de memoria
• Los objetos de memoria es la forma de manejar los datos
• Se clasifican en buffers o imágenes
• Buffers
• Trozos de memoria contíguos (arreglos, punteros,
estructuras)
• Se pueden leer y escribir en ellos
• Imágenes
• Objetos 2D o 3D
• Solo se acceden como read_image() y write_image()
Creando buffers
• Con esta función creamos un buffer para un contexto dado
• Con los flags especificamos:
• La combinación de lectura/escritura permitida en los datos
• El uso de host pinter para guardar los datos
Objetos de memoria
• Los objetos de memoria están asociados con un contexto
• Deben ser explícitamente transferidos a los dispositivos antes de realizar la ejecución
Contexto
Objetos OpenCL no-inicializados (se debe transefir los datos)
Transfiriendo datos
• Los comandos para transferir hacia y desde los dispositivos son:
• clEnqueueRead/WriteBuffer/Image
• Copiando datos desde el anfitrión (host) al dispositivo
(device) es una escritura
• Copiar desde el dispositivo al host es una lectura
• El comando de escritura inicializa el objeto de memoria y lo ubica en un dispositivo
• OpenCL tiene directivas para mapear directamente objetos de memoria a un puntero en el host
Transfiriendo datos
• La función inicializa el objeto de memoria y escribe los datos en el dispositivo asociado con la cola de comandos
• El comando va a escribir datos desde un puntero del host
(ptr) al dispositivo
• El parámetro blocking_write especifica si el comando retorna antes que la transferencia de datos sea completada
• Los eventos especifica que comandos deben ser completados antes que este se ejecute
Transfiriendo datos
• Los objetos de memoria se transfieren a los dispositivos especificando una acción (lectura/escritura) y una cola de comandos
• La especificación de OpenCL deja abierta la validez de objetos en múltiples dispositivos (depende el proveedor)
Contexto
Las imágenes están en el dispositivo pero son parte del
Programas
• En terminos generales, unobjeto programa es una
colección de Kernels OpenCL
• Puede ser código fuente (texto) o un binario compilado
previamente
• Puede contener datos constantes o funciones auxiliares
• Para crear un objeto programa se requiere leer un archivo de texto (código fuente) o un binario compilado
• Para compilar son necesarios los siguientes requerimientos:
• Especificar el dispositivo destino (hay compilación para
cada dispositivo)
• Pasar parámetros al compilador (opcional)
Programas
• Un objeto de programa es creado y compilado cuando se provee los fuentes o un binario
Contexto
Creando un programa
• La función crea un objeto programa usando un archivo de texto conteniendo fuentes
• count especifica la cantidad de líneas en el archivo fuente
• El programador debe crear una función para leer el código
• Si nos las cadenas de caracteres no terminan en NULL, los campos lengths especifican los largos
Compilando un programa
• Esta función compila y linkea un ejecutable desde el objeto programa a cada dispositivo en el ambiente
• En caso de proveer una lista de dispositivos, solo se envía
el ejecutable a los dispositivos en la lista
• El el argumento opcional se puede especificar preprocesador y optimizaciones entre otros
Reportando errores de compilación
• Si hay un error de compilación, OpenCL requiere que el programador adquiera la salida del compilador
• El error se determina por el valor de error retornado por
clBuildProgram()
• Para obtener el mensaje como una cadena de caracteres
se debe llamar a clGetProgramBuildInfo() con el objeto programa y el parámetro CL_PROGRAM_BUILD_STATUS
Kernels
• Un kernel es una función declarada en un programa que es ejecutada en un dispositivo OpenCL
• Un objeto kernel esta compuesto por la función y los
argumentos asociados
• Un objeto kernel es creado desde un programa compilado
• El programa debe asociar explícitamente argumentos (objetos de memoria y primitivas entre otras) con el objeto kernel
Kernels
• El objeto kernel es creado por el objeto programa
Contexto
Creando un Kernel
• Esta función crea un kernel a partir de un objeto programa dado
• El objeto kernel creado es especificado por una cadena de caracteres que coincide con el nombre de la función dentro del programa
Compilación en tiempo de ejecución
• Es costoso compilar programas y crear kernels en tiempo de ejecución
• Lo ideal es hacer estas operaciones una sola vez al comienzo del programa
• Los objetos kernel pueden ser reusados con diferentes argumentos de entrada
cargar código fuente
en un arreglo clCreateProgramWithSource
clCreateProgramWithBinary
Definiendo argumentos del kernel
• Se debe llamar la función clSetKernelArgs
• Se debe especificar el índice del argumento como aparece en la función, el tamaño y el puntero a los datos
Ejemplos argumentos del kernel
/ / p r i m e r ejemplo c l S e t K e r n e l A r g ( k e r n e l , 0 , s i z e o f ( cl_mem ) , ( v o i d ∗)& d_iImage ) ; / / segundo ejemplo c l S e t K e r n e l A r g ( k e r n e l , 1 , s i z e o f ( i n t ) , ( v o i d ∗)& a ) ;Imágenes como argumentos de kernels
• Los objetos de memoria y datos individuales pueden se pasados como argumentos de kernels
Contexto
Estructura de threads
• En los programas masivamente paralelos cada thread computa una parte del problema
• En el caso de una suma de vectores, cada thread va a sumar un elemento del arreglo
• Si lo pensamos de una manera visual, los threads se van a ordenar en la misma forma que los datos
Estructura de threads
• Hagamos una suma simple de vectores de 16 elementos C=A+B 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 A + B = C índices vectores suma vectores
Estructura de threads
• Creamos una estructura de threads 1D para resolver el problema 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 A + B = C threads suma vectores 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Estructura de threads
• Cada thread suma un componente del vector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 A + B = C threads suma vectores 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Estructura de threads
• La estructura de threads esta diseñada para ser escalable
• Cada instancia de un kernel es llamada un ítem de trabajo (puede ser thread)
• Los ítems de trabajo se organizan en grupos de trabajo
• Los grupos de trabajo son independientes entre si (esto permite escalar)
• Un espacio de índices define una jerarquía grupos de trabajo e ítems de trabajo
Estructura de threads
• OpenCL permite identificar a los threads en si mismo y a sus datos
• Los threads pueden determinar el ID global en cada dimensión
• get_global_id(dim)
• get_global_size(dim)
• Los threads pueden determinar el ID del grupo de trabajo y el ID dentro del grupo
• get_group_id(dim)
• get_num_groups(dim)
• get_local_id(dim)
• get_local_size(dim)
• get_global_id(0) = column, get_global_id(1) = row
• get_num_groups(0) * get_local_size(0) == get_global_size(0)
Modelo de Memoria
• OpenCL define varios tipos de memoria
• Esta muy relacionada con la arquitectura del hardware
• Memoria global: accesible a todos los ítems de trabajo
• Memoria constante: solo lectura y global
• Memoria local: local a el grupo de trabajo
Modelo de Memoria
• El manejo de memoria es explicito
• Se debe mover datos desde el host al dispositivo
• Dentro del dispositivo hay que mover los datos desde la memoria global a la local
• Los grupos de trabajo son asignados a ejecutar sobre unidades de cómputo
• No hay garantía de coherencia de datos entre dos grupos de trabajo (no hay mecanismo de software en la
Escribiendo un kernel
• Una instancia de kernel es creada por cada thread
• Los kernels tienen las siguientes características:
• Deben comenzar con la palabra clave __kernel
• Deben tener tipo de retorno void
• Debe declarar el espacio de memoria para cada argumento
que es un objeto de memoria (ahora lo vemos)
• Debe usar las funciones de la API (por ejemplo
get_global_id()) para determinar sobre que datos que cada thread va a trabajar
Identificadores de espacios de memoria
• __kernel toma memoria desde el espacio global de
memoria
• __constant un tipo especial de memoria solo lectura
• __local memoria compartida por el grupo de trabajo
• __private privado a cada ítem de trabajo
• __read_only __read_only usado en imágenes
• Los argumentos del kernel que son objetos de memoria deben ser globales, locales o constantes
Ejemplo de kernel
• Suma simple de dos vectores
_ _ k e r n e l v o i d sumaVectores ( _ _ g l o b a l i n t ∗ A , _ _ g l o b a l i n t ∗ B , _ _ g l o b a l i n t ∗ C) { i n t t i d = g e t _ g l o b a l _ i d ( 0 ) ; C [ t i d ] = A [ t i d ] + B [ t i d ] ; }
Ejecutando el kernel
• Se necesita definir las dimensiones del espacio de índices y los tamaños de los grupos de trabajo
• Los kernels se ejecutan asincónicamente
• clEnqueueNDRangeKernel lo agrega a la cola de ejecución, no garantiza el momento de comienzo
Ejecutando el kernel
• La estructura de los threads es definida por el espacio de índices que es creado
• Cada thread ejecuta el mismo kernel sobre una parte diferente de los datos
Contexto
se crea un espacio de índices relacionado a las dimensiones de los datos
Ejecutando el kernel
• La estructura de los threads es definida por el espacio de índices que es creado
• Cada thread ejecuta el mismo kernel sobre una parte diferente de los datos
Contexto
se crea un espacio de índices relacionado a las dimensiones de los datos
Ejecutando el kernel
• La función le dice al dispositivo asociado con una cola de comandos que comience a ejecutar el kernel
• El espacio global debe ser especificado y el tamaño de los grupos de trabajo local es opcional
• Se puede proveer una lista de eventos que deben cumplirse antes que la operación se ejecute
Copiando datos al anfitrión
• El último paso es copiar los datos desde el dispositivo al CPU
• Similar a la escritura de datos, pero en este caso hay transferencia del dispositivo al CPU
Copiando datos al anfitrión
Liberando recursos
• La matoría de los objetos OpenCL son deben ser liberados luego de ser usados
• Hay una función clRelease{Recurso} para la mayoría de los tipos OpenCL
• Algunos ejemplos son clReleaseProgramm() o clReleaseMemObject()
Verificado errores
• Los comandos OpenCL retornan errores como enteros negativos
• El valor cero significa ejecución exitosa CL_SUCCESS
• Una breve lista de errores:
• -1 CL_DEVICE_NOT_FOUND
• -2 CL_DEVICE_NOT_AVAILABLE
• -3 CL_COMPILER_NOT_AVAILABLE
• -4 CL_MEM_OBJECT_ALLOCATION_FAILURE
Modelo de programación
• Paralelismo de datos
• Mapeo uno-a-uno entre ítems de trabajo y elementos en
objetos de memoria
• Los grupos de trabajo pueden ser definidos explícitamente
o implícitamente (se definen los ítems de trabajo y OpenCL crea los grupos de trabajo)
• Paralelismo de tareas
• El kernel es ejecutado independientemente del espacio de
índices
• Otras formas de paralelismo: enviar a la cola tareas
múltiples, usar tipos de vectores específicos para algún dispositivo particular
• Sinconización
• Es posible entre ítems en un grupo de trabajo
• Es posible entre comandos en una cola de comandos del
Resumiendo
• OpenCL provee herramientas para operar entre el CPU y GPU
• Se necesita crear un contexto para contener toda la información y datos requeridos en un programa OpenCL
• Se crean objetos de memoria para moverlos hacia y desde los dispositivos
• Los programas y los kernels contienen el código que los dispositivos ejecutan
Compilando el primer programa
# Compilando : gcc − I / u s r / i n c l u d e / CL / −lOpenCL \ −o a p p l i c a t i o n V e c t o r A d d i t i o n . cpp # Ejecutando : . / a p p l i c a t i o nEn detalle
j a v i e r @ o r c a : ~ / Downloads / lec02_03_code$ l l t o t a l 16
−rw−r−−r−− 1 j a v i e r j a v i e r 176 Jan 17 2011 v e c t o r a d d . c l −rw−r−−r−− 1 j a v i e r j a v i e r 10092 Jan 17 2011 V e c t o r A d d i t i o n . cpp
j a v i e r @ o r c a : ~ / Downloads / lec02_03_code$ gcc −I / u s r / i n c l u d e / CL / −lOpenCL −o f i r s t −a p p l i c a t i o n −openCL V e c t o r A d d i t i o n . cpp j a v i e r @ o r c a : ~ / Downloads / lec02_03_code$ l l
t o t a l 32
−rwxr−xr−x 1 j a v i e r j a v i e r 12751 Oct 13 15:32 f i r s t −a p p l i c a t i o n −openCL −rw−r−−r−− 1 j a v i e r j a v i e r 176 Jan 17 2011 v e c t o r a d d . c l
−rw−r−−r−− 1 j a v i e r j a v i e r 10092 Jan 17 2011 V e c t o r A d d i t i o n . cpp j a v i e r @ o r c a : ~ / Downloads / lec02_03_code$ . / f i r s t −a p p l i c a t i o n −openCL Running V e c t o r A d d i t i o n program
1 p l a t f o r m s d e t e c t e d P l a t f o r m 0 :
Vendor : NVIDIA C o r p o r a t i o n Name : NVIDIA CUDA 1 d e v i c e s d e t e c t e d Device 0 : Device : NVIDIA C o r p o r a t i o n Name : GeForce GTS 450 No b u i l d e r r o r s Output i s c o r r e c t j a v i e r @ o r c a : ~ / Downloads / lec02_03_code$
NVIDIA Visual Profiler
¡Muchas gracias!
• A. Munshi, B. Gaster, T. G. Mattson, J. Fung, D. Ginsburg, “OpenCL Programming Guide”, Addison-Wesley
Professional, 2011.
• B. Gaster, L. Howes, D. R. Kaeli, P. Mistry, D. Schaa, “Heterogeneous Computing with OpenCL”, Morgan Kaufmann, 2011.
• AMD OpenCL University Kit