Procesamiento Paralelo
OpenCL - Introducci ´onJavier Iparraguirre
Universidad Tecnol ´ogica Nacional, Facultad Regional Bah´ıa Blanca 11 de Abril 461, Bah´ıa Blanca, Argentina
[email protected] http://www.frbb.utn.edu.ar/hpc/
¿Qu ´e es OpenCL?
• OpenCL (Open Computing Language)
• Est ´andar para programar plataformas heterog ´eneas: CPUs, GPUs, DSPs,
• Consta de un lenguaje basado en C99 para armar y una especificaci ´on de la API
• Es soportado por la mayor´ıa (sino todos) en la industria: AMD, Intel, NVIDIA, Apple, Android
Caracter´ısticas
• C ´odigo portable :)
• Se define en cuatro partes
• Modelo de la plataforma
• Modelo de ejecuci ´on
• 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 ´osito 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 ´on se llama dos veces
• La primera llamada se usa para ver la cantidad de plataformas disponibles en la implementaci ´on
• 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 ´on 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 ´an asociados a un contexto:
• Dispositivos
• Objetos de programa: implementaci ´on de los objetos de
c ´omputo
• Kernels: Funciones que corren en dispositivos OpenCL (el
c ´odigo de los threads)
• Objetos de memoria: los datos operados en el dispositivo
(los datos de los threads)
• Colas de comandos: mecanismos de interacci ´on de
dispositivos (transferencia de datos, ejecuci ´on de kernels y sincronizaci ´on)
Contexto
• Cuando se crea un contexto, el programador provee una lista de dispositivos a asociar
Creando un contexto
• Con esta funci ´on 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 ´on a un dispositivo (device)
• Hay transferencia de datos a la memoria y ejecuci ´on de tarea
• Cada dispositivo tiene su cola de comandos
• Los comandos pueden ser sincr ´onicos o asincr ´onicos
• 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 ´on fuera de orden y el sensado de desempe ˜no (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 ´agenes
• Buffers
• Trozos de memoria cont´ıguos (arreglos, punteros,
estructuras)
• Se pueden leer y escribir en ellos
• Im ´agenes
• Objetos 2D o 3D
• Solo se acceden como read image() y write image()
Creando buffers
• Con esta funci ´on creamos un buffer para un contexto dado
• Con los flags especificamos:
• La combinaci ´on de lectura/escritura permitida en los datos
• El uso de host pinter para guardar los datos
Objetos de memoria
• Los objetos de memoria est ´an asociados con un contexto
• Deben ser expl´ıcitamente transferidos a los dispositivos antes de realizar la ejecuci ´on
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 ´on (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 ´on 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 ´ametro 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 ´on (lectura/escritura) y una cola de comandos
• La especificaci ´on de OpenCL deja abierta la validez de objetos en m ´ultiples 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 ´on de Kernels OpenCL
• Puede ser c ´odigo 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 ´odigo fuente) o un binario compilado
• Para compilar son necesarios los siguientes requerimientos:
• Especificar el dispositivo destino (hay compilaci ´on para
cada dispositivo)
• Pasar par ´ametros 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 ´on 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 ´on para leer el c ´odigo
• Si nos las cadenas de caracteres no terminan en NULL, los campos lengths especifican los largos
Compilando un programa
• Esta funci ´on 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 ´on
• Si hay un error de compilaci ´on, 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 ´ametro CL PROGRAM BUILD STATUS
Kernels
• Un kernel es una funci ´on declarada en un programa que es ejecutada en un dispositivo OpenCL
• Un objeto kernel esta compuesto por la funci ´on 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 ´on 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 ´on dentro del programa
Compilaci ´on en tiempo de ejecuci ´on
• Es costoso compilar programas y crear kernels en tiempo de ejecuci ´on
• 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 ´on clSetKernelArgs
• Se debe especificar el ´ındice del argumento como aparece en la funci ´on, el tama ˜no 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 i I m a g e ) ; / / 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 ´agenes 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 ˜nada 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 ´on
• 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
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 ´omputo
• 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 ´agenes
• 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 ˜nos de los grupos de trabajo
• Los kernels se ejecutan asinc ´onicamente
• clEnqueueNDRangeKernel lo agrega a la cola de ejecuci ´on, 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 ´on 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 ˜no de los grupos de trabajo local es opcional
• Se puede proveer una lista de eventos que deben cumplirse antes que la operaci ´on se ejecute
Copiando datos al anfitri ´on
• El ´ultimo 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 ´on
Liberando recursos
• La mator´ıa de los objetos OpenCL son deben ser liberados luego de ser usados
• Hay una funci ´on 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 ´on 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 ´on
• 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 ´ultiples, usar tipos de vectores espec´ıficos para alg ´un dispositivo particular
• Sinconizaci ´on
• 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 ´on 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 ´odigo 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 / l e c 0 2 0 3 c o d e $ 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 / l e c 0 2 0 3 c o d e $ 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 / l e c 0 2 0 3 c o d e $ 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 / l e c 0 2 0 3 c o d e $ . / 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 / l e c 0 2 0 3 c o d e $
NVIDIA Visual Profiler
¡Muchas gracias!
¿Preguntas?
• 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