• Aucun résultat trouvé

4.2 Parall´elisation des noyaux

4.2.2 Restructuration des acc`es-m´emoire

probl`eme. A ce point, nous disposons d’algorithmes pour extraire le parall´elisme amorphe inh´erent` `a chaque noyau. Les tˆaches extraites sont suffisamment fines pour laisser `a l’ordonnanceur la latitude n´ecessaire au r´e´equilibrage dynamique de charges33. Ainsi il nous reste `a g´erer l’aspect data-intensive.

Pour am´eliorer la r´eutilisation des donn´ees en cache et minimiser la latence des acc`es-m´emoire, il faudrait restructurer les insertions et suppressions de donn´ees de chaque noyau. En fait ces indirections m´emoire peuvent impacter de mani`ere significative les performances de ces noyaux34 surtout s’ils

ont une faible intensit´e arithm´etique35. Le probl`eme est qu’il nous est impossible de les inf´erer

puisque les d´ependances de donn´ees varient selon la mani`ere dont la topologie ´evolue36. Dans ce

cas, comment allons nous d´ebrouiller pour restructurer nos acc`es-m´emoire de mani`ere `a att´enuer les p´enalit´es relatives `a ces indirections ?

4.2.2.1 Refonte en vagues synchrones

principe. Afin d’att´enuer leur irr´egularit´e, nous restructurons les noyaux en vagues synchrones en nous inspirant du paradigme multi-bsp37[170]. Ici les instructions d’un noyau sont structur´es en une

s´equence de vagues de tˆaches, chaque vague ´etant constitu´ee d’une phase de calcul local, d’une phase de communication et d’une barri`ere de synchronisation comme montr´e `a la table4.2. Ici l’avantage est triple :

• coalescence : les communications des threads sont regroup´ees en vagues de mani`ere `a att´enuer la latence m´emoire38. Les ´ecritures en m´emoire partag´ee sont ainsi effectu´es de mani`ere coalescente, ce qui est un aspect crucial sur les architectures massivement multithread39.

33`a condition d’ajuster la granularit´e

34C’est particuli`erement vrai pour la simplification comme illustr´e sur la courbe de la figureB.9en annexe. 35L’intensit´e arithm´etique est le ratio de calcul utile sur le nombre d’acc`es de donn´ees. Elle est mesur´ee en flop/byte 36Du coup, nous ne pouvons pas recourir aux techniques usuelles de cache blocking, ni pr´eserver un placement de

donn´ees cache-aware, comme ce qui se fait en visualisation haute performance [57,58]

37Il s’agit d’un bridging-model entre le mat´eriel et l’algorithme : il permet de concevoir des algorithmes tenant compte

des param`etres hardware li´es `a la communication (bande-passante et latence `a chaque niveau de la hi´erarchie m´emoire) tout en restant suffisamment g´en´erique pour la portabilit´e de l’application.

38Plus pr´ecis´ement, elles sont coalesc´ees par thread et effectu´es dans une seule phase d’une vague synchrone. 39C’est particuli`erement vrai pour le gpgpu.

c 2018. HOBY RA K OTO ARIVELO

112 4.2. Parall´elisation des noyaux

• moins de communications : elle permet de r´eduire la fr´equence de mise `a jour de donn´ees entre threads ainsi que les synchronisations associ´ees40.

• portabilit´e : elle offre un juste milieu entre prise en compte des contraintes hardware et g´en´ericit´e pour la portabilit´e des performances. De plus, elle fournit un mod`ele de coˆut li´e aux param`etres hardware, ce qui peut-ˆetre utile pour le r´e´equilibrage de charges41

Table 4.2: Structuration des noyaux.

vagues

noyau 1 2 3 4 5

raffinement : filter steiner apply repair –

simplification : filter graph indep apply repair

relaxation : filter graph match apply repair

lissage : graph color qualit apply –

Contributions

Approach

Main idea

I

explicit parallelism extraction.

- express data dependencies into a graph. - extract a subset of compatible stencils.

- kernel granularity related to its graph size/structure.

I

synchronization for topology consistency.

noyaux extraire topologie extraire normales extraire metriques gradation. repeat raffinement contraction relaxation lissage until convergence

phases d’un noyau repeat filtrer tˆaches construire graphe extraire taches appliquer patterns r´eparer topologie until plus de tˆaches

´etapes BSP cores 1 2 · · · p · · · · · · barrier 1 data fetch2 p · · · · · · data write barrier SLIDE 5/13

synth`ese. Les vagues synchrones relatives `a chaque noyau sont r´esum´ees `a la table4.2. Les d´etails de d´ecomposition des noyaux ainsi que leurs complexit´es th´eoriques42 sont expliqu´es dans[RLP16]43.

Sur cette table, les vagues

• filter correspond au filtrage des points ou mailles actives selon un crit`ere num´erique. • graph correspond `a la construction du graphe de conflits ou mailles `a appairer. • apply correspond `a l’application proprement dite du noyau.

• repair correspond `a l’´epuration des listes d’incidence inconsistantes. • steiner correspond au calcul et r´esolution des indices de points de steiner.

En fait tout l’int´erˆet de ce formalisme r´eside dans la coalescence des communications44 des threads,

ce qui permet de minimiser les synchronisations tout en att´enuant la latence des acc`es-m´emoire. Bon nombre de biblioth`eques existent pour rendre ces communications transparentes [171–174]. N´eanmoins cela est inadapt´e dans notre cas, puisque nous voulons contrˆoler finement la mani`ere dont les transferts de donn´ees sont effectu´es en m´emoire partag´ee.

4.2.2.2 Insertions de mailles

Pour le raffinement, nous disposons de plusieurs patterns de d´ecoupage selon le nombre d’arˆetes ”longues” conform´ement `a une m´etrique donn´ee. Ainsi il est a priori impossible de pr´edire en amont le nombre de cellules `a ins´erer. Pour y rem´edier, une mani`ere simple serait de les stocker localement jusqu’`a ce que chaque thread ait termin´e de traiter toutes les mailles qui lui ont ´et´e assign´ees, puis de les copier de mani`ere synchronis´ee au sein du conteneur partag´e dans une seule vague, comme ce qui est impl´ement´e dans Pragmatic [199,167]. Le probl`eme est que cela induit un nombre important d’acc`es-m´emoire, ce qui peut ˆetre r´edhibitoire en contexte numa.

principe. Pour contourner le probl`eme pr´ec´edent, nous scindons le noyau en deux phases synchrones relatives au filtrage et `a l’application, de sorte `a pouvoir inf´erer explicitement le nombre de cellules `a ins´erer. Ainsi cela va nous permettre de trouver le bon offset d’indices par thread.

Concr`etement, nous stockons le pattern par maille `a appliquer dans un tableau pattern durant la phase de filtrage. Ensuite, chaque thread ti effectue une r´eduction sur pattern dans son espace

d’it´erations dn/pe[i, i + 1], avec n le nombre de tˆaches et p le nombre de threads. Le r´esultat est

40Elle minimise ainsi les contentions d’acc`es aux conteneurs partag´es, typiquement les files de taches associ´e `a chaque

noyau et la structure de donn´ees topologiques

41Dans ce cas, elle permet de d´ecider s’il faut migrer les mailles ou non selon le coˆut estim´e du prochain it´er´e. 42`a l’aide du mod`ele de pont Queuing Shared Memory.

43dans le cas planaire mais c’est exactement pareil en surfacique 44par mise `a jour synchronis´ee de variables en m´emoire partag´ee.

c 2018. HOBY RA K OTO ARIVELO

pour t de 0 `a dlog2ne − 1 faire

pour i de 0 `a n − 1 faire en parall`ele si i < 2talors off[t+1]i ← off [t] i sinon off[t+1]i ← off [t] i + off [t] i−2t fin si barri`ere fin 3 2 1 2 3 2 1 3 3 2 3 1 3 2 3 2 pattern n tasks 10 10 45 0 8 9 9 off 0 8 17 18 0 8 17 35

Figure 4.10: Pr´ecalcul des r´ef´erences par thread pour l’insertion de mailles. Les motifs de raffinement associ´es `a chaque maille sont explicitement r´ef´erenc´es dans un tableau, et chaque thread d´etermine la quantit´e de mailles qu’il doit cr´eer par une r´eduction partielle. Enfin un prefix-sum est effectu´e pour d´eterminer les offsets associ´es `a chaque thread.

Open issue

Data placement

Spatial locality: geometric proximity

) memory locations proximity.

7

index reordering

(Hilbert, Reverse Cuthill-Mckee)

: difficult to parallelize, huge overhead.

7

octree, cache-oblivious

(Packed-memory, Van Emde Boas)

: rebalance and memory costs.

I

workaround: memory block o↵sets precomputation, and asynchronous writes.

cells to be stored

(1 thread per core)

fully asynchronous: 7remote accesses

cells 1 8 2 11 12 3 4 13 14 7 9 6 10 5 16 15

DRAM 1 (close to core 1)

1 8 2 11 12 3 4 13

DRAM 2 (close to core 2)

14 7 9 6 10 5 16 15

block precomputing: 1remote access

cells 1 2 3 4 7 6 5 8 11 12 13 14 9 10 16 15

DRAM 1 (close to core 1)

1 2 3 4 7 6 5 8

DRAM 2 (close to core 2)

11 12 13 14 9 10 16 15

SLIDE 9/13

Figure 4.11: Impact des patterns d’insertion de mailles sur le placement m´emoire en contexteNUMA.

ensuite stock´e dans un tableau offset[i] de taille p. Finalement, un prefix-sum est effectu´e sur offset[i] afin de d´eterminer les plages d’indices [ki, ki+1] par thread.

L’exemple de la figure 4.11 illustre l’impact du pattern d’insertion de mailles sur le placement m´emoire. Ici on a deux threads punais´es sur deux cores situ´es sur deux sockets distincts. Dans le premier cas, les indices des mailles sont obtenus de mani`ere asynchrone et les threads ins`erent directe- ment dans le conteneur partag´e. Dans le second cas, les plages de blocs-m´emoires sont pr´ed´etermin´es dans une premi`ere vague, et les insertions sont r´ealis´ees de mani`ere coalescente dans une vague `a part. Dans ce cas, les blocs de donn´ees associ´es `a chaque thread sont r´eellement stock´es dans la m´emoire la plus proche du core d’une part, et les mailles voisines g´eom´etriquement le sont ´egalement en m´emoire.

4.2.2.3 R´eduction de donn´ees `

A ce point, chaque noyau est structur´e de mani`ere `a ce que la communication des threads se fasse de mani`ere coalescente. De mani`ere concr`ete, elle consiste en une mise `a jour synchronis´ee de donn´ees en m´emoire partag´ee. En fait les points et mailles ainsi que les donn´ees aff´erentes sont stock´ees dans des tableaux unidimensionnels de sorte qu’elles sont r´ef´erenc´ees par des adresses contig¨ues en m´emoire. Dans ce cas, les primitives d’insertion, de suppression ou de mise `a jour des conteneurs

c 2018. HOBY RA K OTO ARIVELO

114 4.2. Parall´elisation des noyaux

partag´es doivent ˆetre synchronis´ees. Ici le but est de montrer comment les r´eductions45 de donn´ees

sont r´eellement effectu´es par les threads.

r´eductions. Les boucles de work-sharing impliqu´ees dans chaque vague synchrone n´ecessitent le recours `a un m´ecanisme de r´eduction de donn´ees au sein d’une file de taches ou d’un conteneur partag´e. Ici les deux points critiques concernent la minimisation des points de synchronisations et la pr´eservation du placement m´emoire initial. En fait il est compliqu´e de concilier les deux : l’asynchronisme implique l’insertion non d´eterministe des donn´ees tandis que le placement fin des donn´ees implique n´ecessairement plus de points de synchronisations. Partant de constat, nous pro- posons deux strat´egies de r´eduction. Elles sont bas´ees sur la pr´ed´etermination des offsets off[tid] `a partir desquels chaque thread tid peut initier sa copie de donn´ees dans le conteneur partag´e R. Ces deux strat´egies sont :

• asynchrone : ici off[tid] est calcul´e de mani`ere non-d´eterministe `a partir de la taille nR du

conteneur partag´e R. `A l’instant t, n[t]R est incr´ement´e de mani`ere atomique tout en r´ecup´erant en cache son ancienne valeur n[t−1] qui sera assign´ee `a off[tid] : ce m´ecanisme s’appelle la cap-

ture atomique. Ainsi le thread tid sait exactement qu’il doit copier ses donn´ees aux indices [n[kR−1], n[kR−1]+|`tid|], o`u `tid d´esigne sa liste locale de donn´ees. Notons qu’on a bien un asyn-

chronisme puisque le thread tid n’attend pas que la r´eduction se termine pour faire du calcul local. Par contre, la plage d’adresses associ´ee `a tid est compl`etement arbitraire et varie d’une ex´ecution `a une autre.

• numa-aware : cette fois off[tid] est mis `a jour par le biais d’un prefix-sum de sorte que les plages d’adresses soient assign´ees de mani`ere d´eterministe aux threads. En fait elles d´ependent de l’indice tid du thread qui lui est punais´e statiquement sur un core : cela permet ainsi d’allouer une plage d’adresses la plus proche possible de ce core. Notons qu’ici on a log(p) r´eductions et donc autant de points de synchronisation.

Listing 4.1: quasi-asynchrone void trigen::fast reduce(int tid,

std::vector<int>* heap, int* shared, int* off, int* size) { int nb = heap[tid].size();

off[tid] = sync fetch and add(size,nb);

// tasks:null si on veut juste calculer les offsets

if(shared != nullptr) memcpy(shared+off[tid],

heap+tid,nb*sizeof(int)); }

Listing 4.2: NUMA-aware void trigen::numa reduce(int tid,

std::vector<int>* heap, int* shared,

int* off) {

int nxt = (tid+1)%n cores; off[nxt] = heap[tid].size(); #pragma omp barrier prefix sum(heap,off); if(shared != nullptr)

memcpy(shared+off[tid],

heap+tid,off[nxt]*sizeof(int)); }

Notons que ces m´ecanismes de r´eduction n’impliquent que des variables enti`eres (heap, shared). Pour minimiser le volume de donn´ees transf´er´ees en m´emoire partag´ee, les r´eductions ne sont utilis´ees que pour la copie d’indices. Ainsi les cellules et les donn´ees qui leur sont associ´ees (normales, tenseurs etc.) sont directement cr´e´es et initialis´ees `a leur emplacement final, contrairement `a l’approche de Rokos [199]. ´Etant donn´e leur caract`ere data-intensive, ces multiples transferts de donn´ees impactent les performances des noyaux de mani`ere significative, notamment en 2D (sec4.2.3).

4.2.3

Consistance des donn´ees d’incidence

`

A ce point nous disposons de moyens pour extraire le parall´elisme amorphe inh´erent `a chaque noyau. En fait nous n’avons g´er´e que les conflits de tˆaches relatives `a la conformit´e de la topologie,

Documents relatifs