OpenHMPP

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.

Introducción

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 concepto del estándar OpenHMPP

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.

El concepto de codelet

Un codelet tiene las siguientes propiedades:

  1. Es una función pura.
    • No contiene declaraciones de variables estáticas o volátiles, y no hace referencia a variables globales, excepto a las variables declaradas como " resident" por una directiva OpenHMPP.
    • no contiene llamadas a funciones con contenido no visible (es decir, no puede estar en línea). Esto incluye el uso de bibliotecas y funciones del sistema como malloc, printf ...
    • Cada llamada a la función debe hacer referencia a una función estática pura (sin punteros de función).
  2. No devuelve ningún valor (el equivalente en C es una función nula; el equivalente en FORTRAN es una subrutina).
  3. El número de argumentos debe ser fijo (sin vararg como en C).
  4. No es recursivo.
  5. Se supone que estos parámetros no tienen alias (en el sentido de puntero).
  6. No contiene directivas callsite(es decir, ninguna llamada RPC a otro codelet) ni otras directivas HMPP.

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.

Ejecución RPC de un codelet

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.

El modelo de memoria implementado

OpenHMPP tiene en cuenta dos espacios de direcciones:

Pautas de HMPP

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.

Pautas de HMPP
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

Concepto de conjunto de directrices

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:

Sintaxis de las directivas OpenHMPP

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:

Sintaxis general

La sintaxis general de las directivas OpenHMPP es:

  • Para el lenguaje C:
#pragma hmpp <grp_label> [codelet_label]? directive_type [,directive_parameters]* [&]
  • Para el idioma FORTRAN:
!$hmpp <grp_label> [codelet_label]? directive_type [,directive_parameters]* [&]

o :

  • <grp_label>es un identificador único que representa un grupo de codelets. Si no se define ningún grupo en la aplicación, esta etiqueta puede estar ausente. Para ser válido, el nombre de la etiqueta debe seguir esta gramática [a-z,A-Z,_][a-z,A-Z,0-9,_]*. Tenga en cuenta que ambos personajes <y >pertenecen a la sintaxis y se les exige en este contexto etiqueta;
  • codelet_labeles un identificador único que nombra un codelet. Para ser válido, el nombre de la etiqueta debe seguir la siguiente gramática:[a-z,A-Z,_][a-z,A-Z,0-9,_]*
  • directive es el nombre de la directiva;
  • directive_parametersdesigna los parámetros asociados con la directiva. Estos parámetros pueden ser de diferentes tipos y especifican argumentos pasados ​​a la directiva o un modo de ejecución (asíncrono o síncrono, por ejemplo);
  • [&] es un carácter utilizado para indicar que la directiva continúa en la siguiente línea (lo mismo para C y FORTRAN).
Configuración de directivas

Los parámetros asociados con una directiva pueden tener varios tipos.

A continuación, los parámetros definidos con OpenHMPP:

  • version = major.minor[.micro]: especifica la versión de las directivas HMPP que el preprocesador tiene en cuenta;
  • args[arg_items].size={dimsize[,dimsize]*}: especifica el tamaño de los parámetros no escalares (una matriz);
  • args[arg_items].io=[in|out|inout]: indica el estado de los argumentos de la función especificada (entrada, salida o ambos). De forma predeterminada, los argumentos no calificados son entradas;
  • cond = "expr": especifica una condición para la ejecución, en forma de una expresión booleana C o FORTRAN que debe ser verdadera para que comience la ejecución del grupo o codelet;
  • target=target_name[:target_name]*: especifica la lista de objetivos a intentar utilizar, siendo el orden de aparición el orden de comprobación de disponibilidad;
  • asynchronous: especifica que la ejecución del codelet no está bloqueando (por defecto, sincrónico);
  • args[<arg_items>].advancedload=true: indica que los parámetros especificados están precargados. Solo se pueden precargar los parámetros con estado in o inout;
  • args[arg_items].noupdate=true: especifica que los datos ya están disponibles en el equipo y que, por tanto, su transferencia no es necesaria. Cuando se establece esta propiedad, no se realiza ninguna transferencia en el argumento considerado;
  • args[<arg_items>].addr="<expr>": es una expresión que proporciona explícitamente la dirección de los datos;
  • args[<arg_items>].const=true: indica que el argumento debe descargarse una sola vez.

Directivas OpenHMPP

Declaración y ejecución de un codelet

Una directiva codeletespecifica que una versión de la siguiente función debe optimizarse para un hardware específico.

Para la directiva codelet :

  • La etiqueta de codelet es obligatoria y debe ser única en la aplicación.
  • La etiqueta de grupo no es obligatoria si no se define ningún grupo.
  • La directiva debe insertarse inmediatamente antes de la declaración de la función en cuestión.

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]?

Directivas aplicadas a las transferencias de datos (optimización de costes adicionales relacionados con las comunicaciones)

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.

  • la directiva "asignar"

Bloquea el hardware y asigna la cantidad necesaria de memoria.

#pragma hmpp <grp_label> allocate [,args[arg_items].size={dimsize[,dimsize]*}]*
  • la directiva de "liberación"

Especifica cuándo se debe publicar el material para un grupo o para un codelet independiente.

#pragma hmpp <grp_label> release
  • la directiva "advancedload"

Carga los datos antes de la ejecución remota del codelet.

#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,]+}]* [,asynchronous]
  • la directiva "delegatedstore"

Constituye una barrera síncrona que permite esperar a que finalice la ejecución de un codelet asíncrono antes de descargar los resultados.

#pragma hmpp <grp_label> [codelet_label]? delegatedstore ,args[arg_items] [,args[arg_items].addr="expr"]* [,args[arg_items].section={[subscript_triplet,]+}]*
  • Cálculos asincrónicos

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:

#pragma hmpp <grp_label> codelet_label synchronize
  • Ejemplo

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

Intercambio de datos entre codelets

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]+

Variable global

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.

Aceleración de la región

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 }

Implementaciones

OpenHMPP se basa en la versión 2.3 de HMPP (Mayo de 2009, Empresa CAPS).

El modelo propuesto por OpenHMPP es implementado por:

  • CAPS Compilers, los compiladores proporcionados por CAPS Entreprise para computación híbrida
  • PathScale ENZO Compiler Suite (compatibilidad con GPU NVIDIA)

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.

Ver también

Referencias