OpenHMPP (HMPP [ 1 ] para Programación Paralela Multinúcleo Híbrida) - estándar de programación para computación heterogénea . Basado en un conjunto de directivas de compilador, este estándar es un modelo de programación diseñado para manejar aceleradores de hardware sin la complejidad asociada a la programación de GPU . Este enfoque basado en directivas se ha implementado porque permite una relación flexible entre el código de una aplicación y el uso de un acelerador de hardware (HWA).
Introducción
El modelo de programación basado en directivas OpenHMPP ofrece una sintaxis para descargar cálculos en aceleradores de hardware y para optimizar el movimiento de datos hacia y desde la memoria del hardware.
El modelo se basa en trabajos iniciados por CAPS (Compiler and Architecture for Embedded and Superscalar Processors), un proyecto común de INRIA , CNRS , la Universidad de Rennes 1 y el INSA de Rennes.
Concepto OpenHMPP
OpenHMPP se basa en el concepto de codelets, funciones que pueden ejecutarse de forma remota en HWAs.
El concepto de codelet de OpenHMPP
Un codelet tiene las siguientes propiedades:
- Es una función pura .
- No contiene declaraciones de variables estáticas o volátiles ni hace referencia a ninguna variable global, excepto si estas han sido declaradas por una directiva HMPP "resident".
- No contiene llamadas a funciones con un cuerpo invisible (que no se puede insertar en línea). Esto incluye el uso de bibliotecas y funciones del sistema como malloc, printf, ...
- Cada llamada a una función debe referirse a una función estática pura (sin punteros a funciones).
- No devuelve ningún valor (función void en C o subrutina en Fortran ).
- El número de argumentos debe ser fijo (es decir, no puede ser una función variádica como en stdarg.h en C).
- No es recursivo.
- Se supone que sus parámetros no tienen alias (véase Aliasing (computing) y Aliasing de punteros ).
- No contiene directivas de sitio de llamada (es decir, RPC a otro codelet) ni otras directivas HMPP.
Estas propiedades garantizan que un RPC de codelet pueda ser ejecutado remotamente por un HWA. Este RPC y sus transferencias de datos asociadas pueden ser asíncronas.
RPC de Codelet
HMPP proporciona RPC síncrono y asíncrono. La implementación del funcionamiento asíncrono depende del hardware.

Modelo de memoria HMPP
HMPP considera dos espacios de direcciones: el del procesador anfitrión y el de la memoria HWA.

Concepto de directivas
Las directivas OpenHMPP pueden considerarse como «metainformación» añadida al código fuente de la aplicación. Se trata de metainformación segura, es decir, no modifican el comportamiento del código original. Abordan la ejecución remota (RPC) de una función, así como la transferencia de datos hacia y desde la memoria HWA.
La siguiente tabla presenta las directivas de OpenHMPP. Estas directivas satisfacen diferentes necesidades: algunas se centran en las declaraciones y otras en la gestión de la ejecución.
Concepto de conjunto de directivas
Uno de los puntos fundamentales del enfoque HMPP es el concepto de directivas y sus etiquetas asociadas, lo que permite exponer una estructura coherente en todo un conjunto de directivas difundidas en una aplicación.
Existen dos tipos de etiquetas:
- Una asociada a un codelet. En general, las directivas que llevan este tipo de etiquetas se limitan a la gestión de un único codelet (denominado codelet independiente en el resto del documento para distinguirlo del grupo de codelets).
- Una etiqueta se asocia a un grupo de fragmentos de código. Estas etiquetas se indican como «<EtiquetaDelGrupo>», donde «EtiquetaDelGrupo» es un nombre especificado por el usuario. En general, las directivas con esta etiqueta se refieren a todo el grupo. El concepto de grupo se reserva para una clase de problemas que requieren una gestión específica de los datos en toda la aplicación para obtener un rendimiento óptimo.
Sintaxis de las directivas de OpenHMPP
Para simplificar la notación, se utilizarán expresiones regulares para describir la sintaxis de las directivas HMPP.
La siguiente convención de colores se utiliza para la descripción de las directivas de sintaxis:
- Las palabras clave HMPP reservadas están en verde ;
- Los elementos gramaticales que pueden declinarse en las palabras clave de HMPP están en rojo ;
- Las variables del usuario permanecen en negro.
Sintaxis general
La sintaxis general de las directivas de OpenHMPP es:
- Para el lenguaje C:
#pragma hmpp < grp_label > [codelet_label]? directive_type [ , directive_parameters ]* [ & ]
- Para el lenguaje FORTRAN:
!$hmpp < grp_label > [codelet_label]? directive_type [ , directive_parameters ]* [ & ]
Dónde:
<grp_label>: es un identificador único que nombra un grupo de fragmentos de código. En los casos en que no se definen grupos en la aplicación, esta etiqueta puede simplemente omitirse. El nombre de la etiqueta válida debe seguir esta gramática:[a-zA-Z_][a-zA-Z0-9_]*. Tenga en cuenta que los caracteres “< >” pertenecen a la sintaxis y son obligatorios para este tipo de etiqueta.codelet_label: es un identificador único que nombra un fragmento de código. El nombre de la etiqueta legal debe seguir esta gramática:[a-zA-Z_][a-zA-Z0-9_]*directive: es el nombre de la directiva;directive_parameters: designa algunos parámetros asociados a la directiva. Estos parámetros pueden ser de diferentes tipos y especificar algunos argumentos dados a la directiva o un modo de ejecución (asíncrono versus síncrono, por ejemplo);[&]: es un carácter que se utiliza para continuar la directiva en la siguiente línea (lo mismo para C y FORTRAN).
Parámetros de la directiva
Los parámetros asociados a una directiva pueden ser de diferentes tipos. A continuación se muestran los parámetros de directiva definidos en OpenHMPP:
version = major.minor[.micro]: especifica la versión de las directivas HMPP que debe tener en cuenta el preprocesador.args[arg_items].size={dimsize[,dimsize]*}: especifica el tamaño de un parámetro no escalar (una matriz).args[arg_items].io=[in|out|inout]: indica que los argumentos de la función especificada son de entrada, de salida o ambos. Por defecto, los argumentos no calificados son de entrada.cond = "expr": especifica una condición de ejecución como una expresión booleana de C o Fortran que debe ser verdadera para iniciar la ejecución del grupo o los codelets.target=target_name[:target_name]*: especifica qué objetivos intentar utilizar en el orden dado.asynchronous: especifica que la ejecución del codelet no es bloqueante (el valor predeterminado es síncrono).args[<arg_items>].advancedload=true: indica que los parámetros especificados están precargados. Solo se pueden precargar parámetros de entrada o salida.args[arg_items].noupdate=trueEsta propiedad especifica que los datos ya están disponibles en el HWA y, por lo tanto, no se necesita ninguna transferencia. Cuando se establece esta propiedad, no se realiza ninguna transferencia en el argumento considerado.args[<arg_items>].addr="<expr>":<expr>es una expresión que proporciona la dirección de los datos a cargar.args[<arg_items>].const=true: indica que el argumento se cargará solo una vez.
Directivas de OpenHMPP
Directivas para declarar y ejecutar un codelet
Una codeletdirectiva declara que un cálculo se ejecutará de forma remota en un acelerador de hardware. Para la codeletdirectiva:
- La etiqueta del codelet es obligatoria y debe ser única en la aplicación.
- La etiqueta de grupo no es necesaria si no se ha definido ningún grupo.
- La directiva codelet se inserta justo antes de la declaración de la función.
La sintaxis de la directiva es:
#pragma hmpp < grp_label > codelet_label ¿ codelet [ , versión = major.minor[.micro]?] [ , args[ arg_items ].io= [[ in | out | inout ]]* [ , args[ arg_items ].size={ dimsize[,dimsize]* } ]* [ , args[ arg_items ].const=true ]* [ , cond = "expr"] [ , objetivo= nombre_objetivo [: nombre_objetivo ]*]
Se pueden agregar varias directivas de codelet a una función para especificar diferentes usos o diferentes contextos de ejecución. Sin embargo, solo puede haber una directiva de codelet para una etiqueta de sitio de llamada determinada.
La callsitedirectiva especifica cómo utilizar un fragmento de código en un punto determinado del programa.
La sintaxis de la directiva es:
#pragma hmpp < grp_label > codelet_label callsite [ , asynchronous ]? [ , args[ arg_items ].size={ dimsize[,dimsize]* } ]* [ , args[ arg_items ].advancedload= [[ true | false ]]* [ , args[ arg_items ].addr=" expr " ]* [ , args[ arg_items ].noupdate=true ]*
Aquí se muestra un ejemplo :
/* declaración del codelet */ #pragma hmpp simple1 codelet, args[outv].io=inout, target=CUDA static void matvec ( int sn , int sm , float inv [ sm ], float inm [ sn ][ sm ], float * outv ){ int i , j ; for ( i = 0 ; i < sm ; i ++ ) { float temp = outv [ i ]; for ( j = 0 ; j < sn ; j ++ ) { temp += inv [ j ] * inm [ i ][ j ]; } outv [ i ] = temp ; } int main ( int argc , char ** argv ) { int n ; ........ /* uso de codelet */ #pragma hmpp sitio de llamadas simple1, args[outv].size={n} matvec ( n , m , myinc , inm , myoutv ); ........ }En algunos casos, se requiere una gestión específica de los datos en toda la aplicación (optimización del movimiento de datos entre CPU y GPU, variables compartidas...).
La groupdirectiva permite declarar un grupo de codelets. Los parámetros definidos en esta directiva se aplican a todos los codelets que pertenecen al grupo. La sintaxis de la directiva es:
#pragma hmpp < grp_label > grupo [ , versión = <mayor>.<menor>[.<micro>]?]? [ , objetivo = nombre_objetivo [: nombre_objetivo ]*]]? [ , cond = “ expr ” ]?
Directivas de transferencia de datos para optimizar la sobrecarga de comunicación
Al utilizar un HWA, el principal cuello de botella suele ser la transferencia de datos entre el HWA y el procesador principal. Para limitar la sobrecarga de comunicación, las transferencias de datos pueden superponerse con ejecuciones sucesivas del mismo fragmento de código mediante la propiedad asíncrona del HWA.
- directiva de asignación
La allocatedirectiva bloquea el HWA y asigna la cantidad de memoria necesaria.
#pragma hmpp < grp_label > allocate [ ,args[ arg_items ].size={ dimsize[,dimsize]* } ]*
- directiva de liberación
La releasedirectiva especifica cuándo liberar el HWA para un grupo o un codelet independiente.
#pragma hmpp < grp_label > release
- directiva de carga avanzada
La advancedloaddirectiva precarga los datos antes de la ejecución remota del fragmento de código.
#pragma hmpp < grp_label > [codelet_label]? advancedload ,args[ arg_items ] [ ,args[ arg_items ].size={ dimsize[,dimsize]* } ]* [ ,args[ arg_items ].addr=" expr " ]* [ ,args[ arg_items ].section={ [ subscript_triplet , ]+ } ]* [ ,asíncrono ]
- directiva de tienda delegada
Esta delegatedstoredirectiva actúa como una barrera de sincronización para esperar a que finalice la ejecución asíncrona de un fragmento de código y, posteriormente, descargar los resultados.
#pragma hmpp < grp_label > [codelet_label]? almacén delegado , args[ arg_items ] [ ,args[ arg_items ].addr=" expr " ]* [ ,args[ arg_items ].section={ [ subscript_triplet , ]+ } ]*
- Computaciones asíncronas
La synchronizedirectiva especifica que se espere hasta que finalice la ejecución de una llamada asíncrona. Para la directiva synchronize, la etiqueta codelet es siempre obligatoria y la etiqueta group es necesaria si el codelet pertenece a un grupo.
#pragma hmpp < grp_label > codelet_label sincronizar
- Ejemplo
En el siguiente ejemplo, la inicialización del dispositivo, la asignación de memoria y la carga de los datos de entrada se realizan solo una vez fuera del bucle y no en cada iteración del mismo.
La synchronizedirectiva permite esperar a que finalice la ejecución asíncrona del fragmento de código antes de iniciar otra iteración. Finalmente, la delegatedstoredirectiva fuera del bucle carga el resultado de sgemm.
int main ( int argc , char ** argv ) { #pragma hmpp sgemm allocate, args[vin1;vin2;vout].size={size,size} #pragma hmpp sgemm advancedload, args[vin1;vin2;vout], args[m,n,k,alpha,beta] for ( j = 0 ; j < 2 ; j ++ ) { #pragma hmpp sgemm callsite, asynchronous, args[vin1;vin2;vout].advancedload=true, args[m,n,k,alpha,beta].advancedload=true sgemm ( size , size , size , alpha , vin1 , vin2 , beta , vout ); #pragma hmpp sgemm synchronize } #pragma hmpp sgemm delegatedstore, args[vout] #pragma hmpp sgemm releaseCompartir datos entre codelets
Esas directivas agrupan todos los argumentos que comparten el mismo nombre para todo el grupo.
Los tipos y dimensiones de todos los argumentos mapeados deben ser idénticos.
La mapdirectiva asigna varios argumentos al dispositivo.
#pragma hmpp < grp_label > mapa, args[ arg_items ]
Esta directiva es bastante similar a la mapanterior, excepto que los argumentos que se van a mapear se especifican directamente por su nombre. La mapbynamedirectiva es equivalente a varias mapdirectivas.
#pragma hmpp < grp_label > mapbyname [ , variableName]+
Variable global
Esta residentdirectiva declara algunas variables como globales dentro de un grupo. Dichas variables pueden ser accedidas directamente desde cualquier fragmento de código perteneciente al grupo. Esta directiva se aplica a la declaración que aparece inmediatamente después en el código fuente.
La sintaxis de esta directiva es:
#pragma hmpp < grp_label > residente [ , args[:: var_name ].io= [[ entrada | salida | entrada/salida ]]* [ , args[:: var_name ].size={ dimsize[,dimsize]* } ]* [ , args[:: var_name ].addr=" expr " ]* [ , args[:: var_name ].const=true ]*
La notación ::var_name con el prefijo ::, indica una variable de la aplicación declarada como residente.
Aceleración de las regiones
Una región es una fusión de las directivas codelet/callsite. El objetivo es evitar la reestructuración del código para construir el codelet. Por lo tanto, todos los atributos disponibles para las directivas codeletor callsitese pueden usar en regionslas directivas.
En lenguaje C:
#pragma hmpp [< MyGroup >] [ label ] region [ , args[ arg_items ].io= [[ in | out | inout ]]* [ , cond = " expr "] < [ , args[ arg_items ].const=true ]* [ , objetivo= nombre_objetivo [ : nombre_objetivo ]*] [ , args[ arg_items ].size={ dimsize[ , dimsize]* } ]* [ , args[ arg_items ].advancedload= [[ true | false ]]* [ , args[ arg_items ].addr=" expr " ]* [ , args[ arg_items ].noupdate=true ]* [ , asíncrono ]? [ , privado=[ arg_items ] ]* { INSTRUCCIONES DE BLOQUE C }
Implementaciones
El estándar abierto OpenHMPP se basa en la versión 2.3 de HMPP (mayo de 2009, CAPS entreprise).
El modelo de programación basado en directivas de OpenHMPP se implementa en:
- Compiladores CAPS, compiladores CAPS Enterprise para computación híbrida.
- Suite de compiladores PathScale ENZO (compatible con las GPU de NVIDIA)
OpenHMPP es utilizado por agentes de computación de alto rendimiento en los sectores de petróleo y gas, energía, manufactura, finanzas, educación e investigación.
Véase también
Referencias
- ↑ Dolbeau, Romain; Bihan, Stéphane; Bodin, François (4 de octubre de 2007). HMPP: Un entorno de programación paralela multinúcleo híbrido (PDF) . Taller sobre procesamiento de propósito general en unidades de procesamiento gráfico . Archivado del original (PDF) el 16 de enero de 2014. Recuperado el 14 de enero de 2014 .
- Interfaces de programación de aplicaciones
- Familia de lenguajes de programación C
- Fortran
- Computación paralela