OpenHMPP Open Standard (HMPP significa Hybrid Multicore Parallel Programming ) es un modelo de programación basado en pautas diseñado para manipular aceleradores de hardware sin preocuparse por la complejidad asociada con la programación de GPU . La elección de la implementación de este modelo recayó en las directivas porque permiten una relación flexible entre el código de la aplicación y el uso de aceleradores de hardware .
NB : Este artículo cubre las directivas OpenHMPP que constituyen el estándar abierto, pero no se ocupa de la ejecución de directivas, vinculadas a la implementación de directivas.
La sintaxis que ofrece el modelo OpenHMPP permite distribuir eficientemente los cálculos en los aceleradores de hardware y optimizar los movimientos de datos desde / hacia la memoria del hardware.
El modelo se basa en el proyecto CAPS (Compiler and Architecture for Embedded and Superscalar Processors) realizado conjuntamente por INRIA, CNRS, Universidad de Rennes 1 e INSA de Rennes.
El estándar OpenHMPP se basa en el concepto de codelet, que es una función que se puede realizar de forma remota en el hardware.
Un codelet tiene las siguientes propiedades:
Estas propiedades garantizan que un codelet RPC se pueda ejecutar de forma remota por hardware. Esta llamada RPC y sus transferencias de datos asociadas pueden ser asincrónicas.
OpenHMPP proporciona un protocolo de llamada a procedimiento remoto sincrónico y asincrónico.
La implementación de la operación asíncrona depende del hardware.
OpenHMPP tiene en cuenta dos espacios de direcciones:
Las directivas OpenHMPP pueden verse como “metainformación” que se agrega al código fuente de la aplicación. Por lo tanto, es metainformación inofensiva, es decir, no cambia el comportamiento original del código.
Abordan la ejecución remota (RPC) de la función, así como las transferencias de datos desde / hacia la memoria de hardware. La siguiente tabla presenta las directivas OpenHMPP, clasificadas según la necesidad abordada: algunas están dedicadas a declaraciones, otras están dedicadas a la gestión de la ejecución.
Instrucciones de control de flujo | Directrices para la gestión de datos | |
---|---|---|
Declaraciones | grupo de codelet | mapa residente mapbyname |
Directivas operativas | región de sincronización del sitio de llamadas | Asignar versión avanzada cargar almacén delegado |
Uno de los puntos fundamentales del enfoque propuesto por el modelo OpenHMPP es el concepto de directivas, asociadas a etiquetas, que permiten configurar una estructura coherente para todo un conjunto de directivas, difundidas en la aplicación.
Hay dos tipos de etiquetas:
Para simplificar las notaciones, se utilizarán expresiones regulares para describir la sintaxis de las directivas OpenHMPP.
También se utilizarán las siguientes convenciones de color:
La sintaxis general de las directivas OpenHMPP es:
o :
Los parámetros asociados con una directiva pueden tener varios tipos.
A continuación, los parámetros definidos con OpenHMPP:
Una directiva codeletespecifica que una versión de la siguiente función debe optimizarse para un hardware específico.
Para la directiva codelet :
La sintaxis de la directiva es:
#pragma hmpp <grp_label> codelet_label codelet [, version = major.minor[.micro]?]? [, args[arg_items].io=[[in|out|inout]]* [, args[arg_items].size={dimsize[,dimsize]*}]* [, args[arg_items].const=true]* [, cond = "expr"] [, target=target_name[:target_name]*]Es posible tener varias directivas codeletpara una función determinada, cada una de las cuales especifica usos diferentes o contextos de ejecución diferentes. Sin embargo, solo puede haber una directiva callsitepara una etiqueta codeletdeterminada.
La directiva callsiteespecifica el uso de un codelet en un punto dado 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]*Un ejemplo a seguir:
/* déclaration du codelet */ #pragma hmpp simple1 codelet, args[outv].io=inout, target=CUDA static void matvec(int sn, int sm, loat 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; ........ /* Utilisation du codelet */ #pragma hmpp simple1 callsite, args[outv].size={n} matvec(n, m, myinc, inm, myoutv); ........ }En algunos casos es necesaria una gestión de datos específica en la aplicación (optimización de movimientos de datos entre CPU y GPU, variables compartidas, etc.).
La directiva grouppermite la declaración de un grupo de codelets. Los parámetros definidos para esta directiva se aplican a todos los codelets asociados con este grupo.
La sintaxis de la directiva es:
#pragma hmpp <grp_label> group [, version = <major>.<minor>[.<micro>]?]? [, target = target_name[:target_name]*]? [, cond = “expr”]?El principal cuello de botella al utilizar HWA suele ser la transferencia de datos entre el hardware y el procesador principal.
Para limitar los costes adicionales vinculados a las comunicaciones, las transferencias de datos pueden ser comunes a ejecuciones sucesivas del mismo codelet utilizando la propiedad asíncrona del equipo.
Bloquea el hardware y asigna la cantidad necesaria de memoria.
#pragma hmpp <grp_label> allocate [,args[arg_items].size={dimsize[,dimsize]*}]*Especifica cuándo se debe publicar el material para un grupo o para un codelet independiente.
#pragma hmpp <grp_label> releaseCarga los datos antes de la ejecución remota del codelet.
Constituye una barrera síncrona que permite esperar a que finalice la ejecución de un codelet asíncrono antes de descargar los resultados.
La directiva synchronizeespecifica que debe esperar la ejecución asincrónica de callsite.
Para esta directiva, la etiqueta de codelet siempre es obligatoria y la etiqueta groupes obligatoria si el codelet está asociado con un grupo. La sintaxis de la directiva es:
En el siguiente ejemplo, la inicialización del hardware, la asignación de memoria y la descarga de datos de entrada se realizan una vez, fuera del ciclo, en lugar de realizarse en cada iteración del ciclo.
La directiva le synchronizepermite esperar el final de la ejecución asincrónica del codelet antes de comenzar otra iteración. Finalmente, la directiva delegatedstore, colocada fuera del bucle, descarga la salida 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 release
Estas directivas permiten compartir todos los argumentos que tienen el mismo nombre para todo un grupo.
Los tipos y dimensiones de todos los argumentos compartidos deben ser los mismos.
La directiva mappermite asociar varios argumentos en el hardware.
#pragma hmpp <grp_label> map, args[arg_items]La directiva mapbynamees similar a la directiva mapexcepto que los argumentos que se mapearán se especifican directamente por nombre. La directiva mapbynamees equivalente a varias directivas map.
La notación es la siguiente:
#pragma hmpp <grp_label> mapbyname [,variableName]+La directiva residentdeclara variables como globales dentro de un grupo.
Se puede acceder directamente a estas variables desde cualquier codelet del grupo en el hardware (de alguna manera se consideran "residentes" en el hardware).
Esta directiva se aplica a la declaración que la sigue en el código fuente.
La sintaxis de esta directiva es:
#pragma hmpp <grp_label> resident [, args[::var_name].io=[[in|out|inout]]* [, 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 aplicación declarada como resident.
Una región es una mezcla de las directivas codelet / callsite. Su objetivo es evitar la reestructuración del código impuesta por la creación explícita de codelets. Por lo tanto, todos los atributos están disponibles para las directivas codeleto callsitepueden usarse para la directiva region.
La sintaxis es la siguiente:
#pragma hmpp [<MyGroup>] [label] region [, args[arg_items].io=[[in|out|inout]]* [, cond = "expr"]< [, args[arg_items].const=true]* [, target=target_name[:target_name]*] [, args[arg_items].size={dimsize[,dimsize]*}]* [, args[arg_items].advancedload=[[true|false]]* [, args[arg_items].addr="expr"]* [, args[arg_items].noupdate=true]* [, asynchronous]? [, private=[arg_items]]* { C BLOCK STATEMENTS }OpenHMPP se basa en la versión 2.3 de HMPP (Mayo de 2009, Empresa CAPS).
El modelo propuesto por OpenHMPP es implementado por:
Además, OpenHMPP se utiliza en el marco de proyectos HPC realizados en campos como el petróleo, la energía, la industria, la educación y la investigación y permite desarrollar versiones de alto rendimiento de sus aplicaciones, conservando el código ya producido.