OpenMP

De Viquipèdia
Salta a: navegació, cerca
Infotaula de programariOpenMP
OpenMP logo.png
Tipus estàndard tècnic i programari
Més informació
Lloc web Web oficial
Modifica dades a Wikidata

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.[1]
  • A l'octubre de 1998 van publicar l'estàndard C/C++.[2]
  • L'any 2000 va sortir la versió 2.0 de l'estàndard Fortran.[3]
  • L'any 2002 va sortir la versió 2.0 de l'estàndard C/C++.[4]
  • L'any 2005 va sortir la versió 2.5. És un estàndard combinat de C/C++ i Fotran.[5]
  • Al maig de 2008, va sortir la versió 3.0.[6]
  • Al juliol de 2013 es va publicar la versió 4.5. És la versió actual de les especificacions de l'API.[7]

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 compilador 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. El thread encarregat de crear la resta s'anomena master amb id 0. Un cop realitzat el fork, 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. També és destacable que totes les variables creades a l'interior d'aquesta construcció seran creades per a cada thread.

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: Utilitzat per repartir iteracions de bucles entre diferents threads.
  • section: Utilitzat per distribuir un bloc de codi a un únic thread.
  • single: Especifica que un tros de codi serà executat per un únic thread, amb un barrier implícit al final.
  • master: Especifica que un tros de codi serà executat per un únic thread, que serà el master. No conté un barrier al final.

Clàusules[modifica | modifica el codi]

Clàusules de compartició de dades[modifica | modifica el codi]

  • 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.
  • 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 no, on la variable serà tractada com a privada o compartida. Per defecte, el valor de la clàusula default serà shared.
  • 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ó.

Clàusules de sincronització[modifica | modifica el codi]

  • critical: El bloc de codi s'executa per un únic thread en cada moment, fet que evita condicions de carrera.
  • atomic: Les actualitzacions en memoria de la següents instrucció s'executen atòmicament, és a dir, microinstrucció a microinstrucció. El compilador utilitza instruccions hardware que proporcionen un millor rendiment que utilitzant critical
  • ordered: El block s'executa en l'ordre en que les iteracions s'executarien en un bucle seqüencial.
  • barrier: Cada thread espera que la resta acabi la seva execució. Les directives de compartició de treball contenen un barrier implícit.
  • nowait: Especifica que els threads que acabin el treball assignat no han d'esperar a que la resta finalitzin la seva execució. En absència d'aquesta clàusula, els threads esperaran a l'execució de la resta.

Clàusules de planificació[modifica | modifica el codi]

  • schedule(type, chunk): Utilitzat en directives de compartició de treball per definir el mètode utilitzat per repartir iteracions d'un bucle entre diferents threads.
    • static: Les iteracions del bucle són assignades a cada thread abans de l'execució. Per defecte, les iteracions es reparteixen de forma equitativa entre tots els threads, però mitjançant el paràmetre chunk especifiquem el nombre chunk d'iteracions que assignarem a un thread.
    • dynamic: S'assignen una sèrie d'iteracions als threads, que quan acaben l'execució, obté noves iteracions per a realitzar. El paràmetre chunk especifica quantes iteracions s'assignen a cada thread cada vegada.
    • guided: S'assigna una gran quantitat d'iteracions a cada thread, que va disminuint a mida que avança l'execució. D'aquesta manera, cada thread rep menys treball cada vegada que en demana més. El paràmetre chunk indica quin es el nombre mínim d'iteracions que pot arribar a rebre cada thread.

Control amb if[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.

Còpia de dades[modifica | modifica el codi]

  • 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 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.
  • copyprivate: Utilitzat amb single per copiar valors d'objectes privats d'un thread a objectes d'altres threads.

Distribució física de threads[modifica | modifica el codi]

  • 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.

Definició del número de threads[modifica | modifica el codi]

  • num_treads(scalar-integer-expression): Permet definir el nombre de threads que executaran el codi de la regió paral·lela.

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.

Implementacions[8][modifica | modifica el codi]

Compiladors amb implementació d'OpenMP 3.0:

  • GCC 4.3.1
  • Compilador Mercurium
  • Compiladors Intel Fortran i C/C++, versions 11.0 i 11.1 i Intel C/C++ and Fortran Composer XE 2011 and Intel Parallel Studio.
  • Compilador IBM XL C/C++
  • L'actualització 1 de Sun Studio 12 té una implementació completa d'OpenMP 3.0

Nombrosos compiladors soporten OpenMP 3.1:

Diferents compiladors soporten OpenMP 4.0:

  • GCC 4.9.0 per C/C++, GCC 4.9.1 per Fortran
  • Compiladors Intel Fortran i C/C++ 15.0
  • LLVM/Clang 3.7 (partial)

Compiladors auto-paral·lelitzadors que generen codi font amb directives d'OpenMP:

  • iPat/OMP
  • Parallware
  • PLUTO
  • ROSE(Compiler framework)
  • S2P by KPIT Cummins Infosystems Ltd.

Diversos profilers i debuggers també soporten OpenMP:

  • Allinea Distributed Debugging Tool (DDT)
  • Allinea MAP
  • ompP
  • VAMPIR

Avantatges i inconvenients[modifica | modifica el codi]

Avantatges[modifica | modifica el codi]

  • Codi multithreading portable.
  • Simple, ja que no necessita pas de missatges com a MPI.
  • La descomposició de dades es gestionada per directives automàticament.
  • Escalabilitat, si ho comparem amb MPI, en sistemes de memòria compartida.
  • Pot treballar només en una part del programa, i no necessita que el codi original sigui modificat pel que es redueix la probabilitat d'introduir bugs.
  • Codi unificat per aplicacions sèrie i paral·leles. Les directives d'OpenMP són tractades com a comentaris si no s'especifica el flag corresponent.
  • Permet paral·lelisme amb granularitat fina o gruixuda, depenent de les necessitats i l'entorn de cada aplicació.
  • Pot ser utilitzat en acceleradors com GPGPU.

Inconvenients[modifica | modifica el codi]

  • Risc d'introduir bugs en debugging i condicions de carrera.
  • La seva execució només es eficient en multiprocessadors amb memòria compartida.
  • Necessita un compilador que suporti OpenMP (gcc i icc en serien bons exemples).
  • L'escalabilitat es superior a la de MPI però ve determinada per l'arquitectura de memoria.
  • No té suport per a compare-and-swap.
  • No té un bon tractament d'errors.
  • És possible reduir el rendiment fàcilment amb un ús compartit fals (false sharing).

Referències[modifica | modifica el codi]

  1. «OpenMP Fortran V 1.0».
  2. «OpenMP C/C++ V 1.0» (en anglès).
  3. «OpenMP Fortran V 2.0».
  4. «OpenMP C/C++ V 2.0» (en anglès).
  5. «OpenMP C/C++ i Fortran V 2.5» (en anglès).
  6. «OpenMP C/C++ i Fortran V 3.0» (en anglès).
  7. «OpenMP C/C++ i Fortran V 4.5» (en anglès).
  8. «OpenMP Compilers - OpenMP» (en en-gb). [Consulta: 14 juliol 2017].

Enllaços externs[modifica | modifica el codi]

Articles i guies