O OpenHMPP Open Standard (HMPP significa Hybrid Multicore Parallel Programming ) é um modelo de programação baseado em diretrizes projetado para manipular aceleradores de hardware sem se preocupar com a complexidade associada à programação de GPU . A escolha da implementação para este modelo recai sobre as diretivas, pois permitem uma relação frouxa entre o código do aplicativo e o uso de aceleradores de hardware .
NB : Este artigo aborda as diretivas OpenHMPP que constituem o Padrão Aberto, mas não trata da execução de diretivas, vinculada à implementação de diretivas.
A sintaxe oferecida pelo modelo OpenHMPP permite distribuir com eficiência os cálculos nos aceleradores de hardware e otimizar os movimentos de dados de / para a memória do hardware.
O modelo é baseado no projeto CAPS (Compilador e Arquitetura para Processadores Embarcados e Superescalares) realizado em conjunto pelo INRIA, CNRS, Universidade de Rennes 1 e INSA de Rennes.
O padrão OpenHMPP é baseado no conceito de codelet, que é uma função que pode ser executada remotamente no hardware.
Um codelet tem as seguintes propriedades:
Essas propriedades garantem que um codelet RPC possa ser executado remotamente por hardware. Esta chamada RPC e suas transferências de dados associadas podem ser assíncronas.
OpenHMPP fornece um protocolo de chamada de procedimento remoto síncrono e assíncrono.
A implementação da operação assíncrona depende do hardware.
O OpenHMPP leva em consideração dois espaços de endereço:
As diretivas OpenHMPP podem ser vistas como “metainformações” que são adicionadas ao código-fonte do aplicativo. É, portanto, meta-informação inofensiva, ou seja, não altera o comportamento original do código.
Eles tratam da execução remota (RPC) da função, bem como das transferências de dados de / para a memória do hardware. A tabela a seguir apresenta as diretivas OpenHMPP, classificadas de acordo com a necessidade abordada: algumas são dedicadas a declarações, outras são dedicadas à gestão da execução.
Instruções de controle de fluxo | Diretrizes para gerenciamento de dados | |
---|---|---|
Afirmações | grupo de codelet | mapa residente mapbyname |
Diretrizes operacionais | região de sincronização de callite | alocar release advancedload delegatedstore |
Um dos pontos fundamentais da abordagem proposta pelo modelo OpenHMPP é o conceito de diretivas, associadas a rótulos, que permitem configurar uma estrutura coerente para todo um conjunto de diretivas, disseminadas na aplicação.
Existem dois tipos de rótulos:
Para simplificar as notações, expressões regulares serão usadas para descrever a sintaxe das diretivas OpenHMPP.
As seguintes convenções de cores também serão usadas:
A sintaxe geral para as diretivas OpenHMPP é:
ou :
Os parâmetros associados a uma diretiva podem ter vários tipos.
A seguir, os parâmetros definidos com OpenHMPP:
Uma diretiva codeletespecifica que uma versão da função a seguir deve ser otimizada para um hardware especificado.
Para a diretiva codelet :
A sintaxe da diretiva é:
#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]*]É possível ter várias diretivas codeletpara uma determinada função, cada uma especificando diferentes usos ou diferentes contextos de execução. No entanto, só pode haver uma diretiva callsitepara um codeletdeterminado rótulo .
A diretiva callsiteespecifica o uso de um codelet em um determinado ponto do programa.
A sintaxe da diretiva é:
#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]*Um exemplo 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); ........ }Em alguns casos, o gerenciamento de dados específicos é necessário na aplicação (otimização dos movimentos de dados entre CPU e GPU, variáveis compartilhadas, etc.).
A diretiva grouppermite a declaração de um grupo de codelets. Os parâmetros definidos para esta diretiva são aplicados a todos os codelets associados a este grupo.
A sintaxe da diretiva é:
#pragma hmpp <grp_label> group [, version = <major>.<minor>[.<micro>]?]? [, target = target_name[:target_name]*]? [, cond = “expr”]?O principal gargalo ao usar o HWA geralmente são as transferências de dados entre o hardware e o processador principal.
Para limitar os custos adicionais vinculados às comunicações, as transferências de dados podem ser comuns a execuções sucessivas do mesmo codelet usando a propriedade assíncrona do equipamento.
Ele bloqueia o hardware e aloca a quantidade necessária de memória.
#pragma hmpp <grp_label> allocate [,args[arg_items].size={dimsize[,dimsize]*}]*Ele especifica quando o material deve ser liberado para um grupo ou para um codelet independente.
#pragma hmpp <grp_label> releaseEle carrega os dados antes da execução remota do codelet.
Constitui uma barreira síncrona que permite aguardar a conclusão da execução de um codelet assíncrono antes de baixar os resultados.
A diretiva synchronizeespecifica que você deve aguardar a execução assíncrona do callsite.
Para esta diretiva, o rótulo do codelet é sempre obrigatório e o rótulo groupé obrigatório se o codelet estiver associado a um grupo. A sintaxe da diretiva é:
No exemplo a seguir, a inicialização do hardware, a alocação de memória e o download dos dados de entrada são feitos uma vez, fora do loop, em vez de em cada iteração do loop.
A diretiva synchronizepermite que você aguarde o final da execução assíncrona do codelet antes de iniciar outra iteração. Finalmente, a diretiva delegatedstore, colocada fora do loop, baixa a saída 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
Essas diretivas permitem compartilhar todos os argumentos com o mesmo nome para um grupo inteiro.
Os tipos e dimensões de todos os argumentos compartilhados devem ser os mesmos.
A diretiva mappermite associar vários argumentos no hardware.
#pragma hmpp <grp_label> map, args[arg_items]A diretiva mapbynameé semelhante à diretiva, mapexceto que os argumentos a serem mapeados são especificados diretamente pelo nome. A diretiva mapbynameé equivalente a várias diretivas map.
A notação é a seguinte:
#pragma hmpp <grp_label> mapbyname [,variableName]+A diretiva residentdeclara variáveis como globais dentro de um grupo.
Essas variáveis podem ser acessadas diretamente de qualquer codelet do grupo no hardware (elas são de certa forma consideradas "residentes" no hardware).
Esta diretiva se aplica à declaração que a segue no código-fonte.
A sintaxe desta diretiva é:
#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]*A notação ::var_name, com o prefixo ::, indica uma variável de aplicativo declarada como resident.
Uma região é uma mistura das diretivas codelet / callite. Seu objetivo é evitar a reestruturação do código imposta pela criação explícita de codelets. Portanto, todos os atributos disponíveis para diretivas codeletou callsitepodem ser usados para a diretiva region.
A sintaxe é a seguinte:
#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 é baseado na versão 2.3 do HMPP (Maio de 2009, Empresa CAPS).
O modelo proposto pela OpenHMPP é implementado por:
Além disso, OpenHMPP é utilizado no âmbito de projetos de HPC realizados em áreas como petróleo, energia, indústria, educação e pesquisa e permite desenvolver versões de alto desempenho de suas aplicações, preservando o código já produzido.