OpenMP

De Viquipèdia
Dreceres ràpides: navegació, cerca

OpenMP (Open Multi-Processing) és una interfície de programació d'aplicacions (API) que suporta programació multiprocés amb memòria compartida multi-plataforma en C/C++ i Fortran a moltes arquitectures, incloent les plataformes Unix i Microsoft Windows. Consisteix en un conjunt de directives de compilador, rutines de biblioteques, i variables d'entorn que afecten al comportament en temps d'execució.

Definit conjuntament per un grup dels principals fabricants de maquinari i programari, OpenMP és un model portable i escalable que dóna als programadors una interfície simple i flexible per a desenvolupar aplicacions paral·leles per a plataformes que van des de l'escriptori fins als supercomputadors.

Una aplicació construïda amb el model híbrid de programació paral·lela pot executar-se en un raïm d'ordinadors fent servir OpenMP i Message Passing Interface (MPI).

Introducció[modifica | modifica el codi]

OpenMP és una implementació de multithreading, un mètode de paral·lelització pel qual el "thread" (fil, en anglès) mestre (una sèrie d'instruccions executades consecutivament) es "bifurca" en un nombre determinat de "threads" esclaus i la tasca es divideix entre ells. Els threads llavors s'executen concurrentment, amb l'entorn d'execució ubicant els threads a diferents processadors.

La secció de codi que es vol que s'executi en paral·lel és marcada corresponentment, amb una directiva de preprocessador que farà que els threads es formin abans que la secció sigui executada. Cada thread té un "id" (identificador) associat que pot ser obtingut fent servir una funció (anomenada omp_get_thread_num() a C/C++ i OMP_GET_THREAD_NUM() a Fortran). L'id de thread és un enter, i el thread mestre té una id de "0". Després de l'execució del codi paral·lelitzat, els threads s'"uneixen" de nou al thread mestre, que continua endavant fins al final del programa.

Per defecte, cada thread executa la secció de codi paral·lelitzada independentment. Es poden fer servir "construccions de compartició de treball" per a dividir una tasca entre els threads de forma que cada thread executi la part de codi que té assignada. Fent servir OpenMP d'aquesta forma es pot aconseguir tant el paral·lelisme de tasques com el paral·lelisme de dades.

L'entorn d'execució assigna els threads a processadors depenent de l'ús, la càrrega de la màquina i altres factors. El nombre de threads pot ser assignat per l'entorn d'execució basant-se en variables d'entorn o en codi fent servir funcions. Les funcions OpenMP estan incloses en un fitxer de capçalera amb el nom "omp.h" a C/C++.

Història[modifica | modifica el codi]

  • L'OpenMP Architecture Review Board (ARB) va publicar el seu primer estàndard, OpenMP per a Fortran 1.0, a l'octubre de 1997.
  • A l'octubre de 1998 van publicar l'estàndard C/C++.
  • L'any 2000 va sortir la versió 2.0 de l'estàndard Fortran.
  • L'any 2002 va sortir la versió 2.0 de l'estàndard C/C++.
  • L'any 2005 va sortir la versió 2.5. És un estàndard combinat de C/C++ i Fotran.
  • Al maig de 2008, va sortir la versió 3.0. És la versió actual de les especificacions de l'API.

Interfície d'usuari[modifica | modifica el codi]

Directives de compilació[modifica | modifica el codi]

A OpenMP es troben una sèrie de construccions de control i d'atributs de dades, que s'estenen al llenguatge base (F77, f90, C i C++). Els compiladors, per defecte, ometen aquestes directives OpenMP si no s'activen mitjançant un flag "-mp" o "-fopenmp".

Biblioteca i variables d'entorn[modifica | modifica el codi]

OpenMP disposa d'un conjunt de funcions per controlar paràmetres incloses a la llibreria de C "omp.h". Un exemple seria la definició del número de threads mitjançant call omp_set_num_threads(64). Per una altra banda, disposa de variables d'entorn que permeten controlar paràmetres de forma diferent, com per exemple setenv OMP_NUM_THREADS(8).

Elements principals[modifica | modifica el codi]

Els elements principals d'OpenMP són directives per a la creació de threads, distribució de treball, gestió de dades en l'entorn, sincronització de threads, rutines d'execució a nivell d'usuari i variables d'entorn. Aquestes directives a C/C++ s'anomenen #pragmas.

Directives vectorials[modifica | modifica el codi]

En un primer nivell, podem diferenciar les directives SIMD o vectorial, destinades a paral·lelisme a nivell de dades. La directiva #pragma omp simd, indica al compilador que el bucle següent es vectoritzable.

Segons la versió del compilador apuesta directiva pot ser ignorada, que el processador trobi errades en dependències de dades que impedeixin la paral·lelització d'aquest o bé que utilitzi aquesta directiva com a pista per a utilitzar instruccions vectorials. La directiva #pragma omp declare simd indica al compilador que ha de generar una versió SIMD d'una funció que podrà ser cridada des d'un bucle vectoritzat. Per tant, la funció declarada amb aquesta directiva podrà també ser executada sense necessitat d'aplicar paral·lelisme de dades.

Tot i que no ho indiquem explícitament, el compliador analitza el codi i tracta de vectoritzar els bucles de forma automàtica (sempre que s'indiqui al compilador un nivell d'optimització adequat mitjançant clags com -O3 o Ofast). Per comprobar que realment el compilador ha vectoritzat un bucle, podem analitzar el codi màquina on descobrirem una p als mnemònics de les instruccions assembler, fet que confirma la utiltzació de paralel·lisme SIMD.

Creació de threads[modifica | modifica el codi]

La directiva #pragma omp parallel, basada en un model d'execució FORK-JOIN que indica al compilador que el codi serà executat per diferents threads, creant així una regió paral·lela. D'aquesta manera, el compilador reparteix trossos d'iteracions del següent bucle als diferentes threads. Per defecte la variable d'iteració del bucle és privada per a cada thread però cal tenir en compte que si no s'especifica el contrari, la resta de variables del codi sera compartides per tots els threads. El thread encarregat de crear la resta s'anomena master amb id 0.

Directives de compartició de treball[modifica | modifica el codi]

Les directives de compartició de treball són utilitzades per a especificar com es reparteix el treball a un o tots els threads.

  • for
  • section
  • single
  • master

Clàusules[modifica | modifica el codi]

  • if([ parallel :] scalar-logical-expression): Permet establir una expressió tenera, que segons si s'evalua com a 1 o un 0, executarà el codi de la regió paral·lela en paral·lel o de forma seqûencial, respectivament.
  • num_treads(scalar-integer-expression): Permet definir el nombre de threads que executaran el codi de la regió paral·lela.
  • default(shared | none): Es pot definir com a shared que significa que qualsevol variable de la regió paral·lela es tractarà com si estigués a l'interior d'una clàusula shared o none, on caldrà especificar cada variable com a privada o compartida. Per defecte, el valor de la clàusula default serà shared.
  • private(list): Especifica que cada subprocés ha de tenir la seva propia instància d'una variable. Totes les variables privades estaran inicialitzades a 0.
  • firstprivate(list): Realitza la mateixa funció que la clàusula private, però inicialitza la variable privada amb el valor de la variable del thread master.
  • lastprivate(list): Les variables de la lista s'inicialitzen a 0 i la variable del thread master és actualitzada amb el valor de l'última iteració a realitzar.
  • shared(list): Especifica que les variables incloses a la llista són compartides per tots els threads.
  • copyin(list): Realitza la mateixa funció que la clàusula private, però inicialitza la variable privada amb el valor de la variable del thread master. És útil en variables definides mitjançant #pragma omp threadprivate(list) que permet definir variables privades associades a cada thread d'una regió paral·lela que mantindran el mateix valor cada cop que el thread associat sigui creat.
  • reduction(reduction-identifier: list): Especifica que les variables de la llista estan subjectes a un operador de reducció com sum, min/max o count. La reducció es pot implementar amb una crida a funció.
  • proc_bind(master | close | spread): Controla el thread affinity dels threads. La opció master obliga a tots els threads del team a ser assignats al mateix lloc del thread master. Amb l'opció close, els threads són assignats a llocs propers al del thread master. En cas que siguie spread, els threads s'assignen de forma dispersa.

Trobem també directives per repartir feina com #pragma omp for , que es el bucle paral·lel per excel·lència. Les iteracions es distribueixen entre els threads existents a la regió paral·lela i l'índex del bucle es privat. Aquesta directiva accepta les clàusules private, lastprivate, firstprivate i reduction prèviament anomenades, a més de nowait, ordered, collapse, schedule.

Variables d'entorn[modifica | modifica el codi]

Les variables d'entorn són un mètode per alterar les característiques d'execució de les aplicacions que utilitzen OpenMP, com per exemple el número de threads que es crearan per defecte a les regions paral·leles mitjançant OMP_NUM_THREADS.

Rutines d'execució a nivell d'usuari[modifica | modifica el codi]

Implementacions[modifica | modifica el codi]

Avantatges i inconvenients[modifica | modifica el codi]

Enllaços externs[modifica | modifica el codi]

Articles i guies