• Aucun résultat trouvé

Paquetage et dépaquetage des arrays pour le traitement SIMD

4.4 Primitives du calcul comme skeletons algorithmiques

4.4.3 Paquetage et dépaquetage des arrays pour le traitement SIMD

Nous appelons paquetage d’un array 1D la transformation d’un array 1D en un autre array 1D dont les éléments sont les vecteurs paquetés (PVec). Dans la littérature on parle aussi de vectorisation des données (dérivée d’un terme anglais vectorize).

4.4.3.1 Paquetage d’un array 1D

La transformation de paquetage est triviale dans le cas d’un array 1D où l’on n’a qu’un seul axe à vectoriser et le procédé est ainsi très simple et direct. Malgré cela, nous tenons à décrire ici cette opération par l’approche fonctionnelle, car nous pouvons ainsi démontrer, sur un exemple trivial, la construction, l’indexation et le travail avec les arrays dans Haskell.

La fonction mkAr1DPVec définit cette transformation :

mkAr1DPVec :: I → Ar I α → Ar I ( PVec I α) mkAr1DPVec n ar = array bndsnew

[ ( i , pvec (1,n) [ (k ,ar ! ( lo + ( i−lo) ∗ n+(k−1))) | k ← [ 1 .. n] ] ) | i ← range bndsnew]

where

( lo , hi ) = bounds $ ar

bndsnew= ( lo , lo−1+(div ( hi−lo+1) n) )

Elle prend deux paramètres. Le premier, n, définit le nombre d’éléments qui seront paquetés dans les éléments PVec de l’array de sortie. Le deuxième paramètre ar est l’array d’entrée, remarquons que sa taille doit être divisible par n. La manière de découpage de l’array d’entrée est illustré sur la fig. 4.3

lo lo+n lo+2n hi

FIG. 4.3 : Découpage d’un array lors de sa transformation à un array paqueté, nombre d’éléments dans un

élément paqueté n = 2

4.4.3.2 Paquetage d’un array 2D

Le passage d’un array de 2D ou de plusieurs dimensions à un array de la même dimension com- posé de vecteurs paquetés PVec nécessite le choix de l’axe principal pour la vectorisation. Le choix simple est prédéfini par la manière dont les données sont stockées dans la mémoire. Sur les processeurs GPP/GPPMM, toutes les données, y compris les structures nD, sont stockées dans un espace linéaire 1D et accédées ainsi. L’accès par les instructions SIMD que nous utilisons sur les architectures GPPMM pour la lecture et la sauvegarde des données n’en fait pas exception. Si nous souhaitons utiliser ces ins- tructions dans nos algorithmes, nous sommes contraints dans notre choix de l’axe de vectorisation de nos données par le sens de leur stockage dans la mémoire.

En revanche, si nous souhaitons paqueter les données d’un array dans l’axe perpendiculaire à celui de la mémoire, notre travail exige une autre approche car nous ne pouvons pas accéder à ces données directement par les instructions SIMD. Nous sommes obligés d’utiliser soit la lecture élément par élé- ment, soit une autre approche qui nous permettrait d’interpréter correctement les données lues dans un

fst snd

(a)array 2D avant découpage fs t snd (b)découpage par la direction de la première coordonnée (fst) fs t snd (c) découpage par la direction de la deuxième coordonnée (snd)

FIG. 4.4 : Exemple de vectorisation d’un array 2D pour différentes versions de découpage et la taille du vecteur

paqueté n = 2

sens différent. Nous montrerons les algorithmes implémentant cette approche dans le chapitre 6 dédié à la permutation des arrays, page 127.

Nous allons présenter deux possibilités de vectorisation d’un array 2D : le paquetage par l’axe de la première coordonnée défini par la fonction mkAr2DPVecByFst et le paquetage dual par l’axe de la deuxième coordonnée par la fonction mkAr2DPVecBySnd. Pour présenter une définition générique et indépendante d’un système de coordonnées dans l’image, nous ne parlons pas des coordonnées x ou y d’une image mais nous utilisons la notation matricielle et parlons des coordonnées première et deuxième, exprimées par les suffixes Fst ou Snd respectivement.

D’un point de vue pratique, ces définitions ne correspondent pas à un algorithme concret. Il s’agit, en effet, de la formalisation du changement de la perception des données stockées dans la mémoire et du changement de leur mode d’adressage. On peut dire que ces définitions correspondent au changement du type des données à partir d’un array du type Ar (I, I) α à un array du type Ar (I, I) (PVec I α) dont les éléments sont les vecteurs paquetés, exactement comme c’est inscrit dans la signature de type de ces fonctions.

La fonction mkAr2DPVecByFst définit la vectorisation par l’axe de la première coordonnée et la manière dont l’array est découpé en données paquetées est illustrée sur la fig. 4.4(b)

mkAr2DPVecByFst :: I → Ar ( I , I ) α → Ar ( I , I ) ( PVec I α) mkAr2DPVecByFst n ar = array bndsnew

[ ( ( i , j ) , pvec (1,n) [ (k ,ar ! ( flo + ( i−flo) ∗ n+(k−1),j ) ) | k ← [ 1 .. n] ] ) | ( i , j ) ← range bndsnew]

where

( ( flo , slo ) , ( fhi , shi ) ) = bounds $ ar

bndsnew= ( ( flo , slo ) , ( flo −1+(div ( fhi −flo+1) n) , shi ) )

Cette fonction crée un nouvel array par la fonction array. Cet array a une dimension réduite dans l’axe de paquetage. Les éléments de cet array sont les vecteurs paquetés PVec, créés par la fonction pvec. La fonction bounds est utilisée pour obtenir les limites de l’array d’entrée ar. La variable bndsnew détient les nouvelles limites de l’array de sortie.

La fonction similaire, mkAr2DPVecBySnd, définit la vectorisation par l’axe de la deuxième coor- donnée et la manière de découper est illustrée sur la fig. 4.4(c)

mkAr2DPVecBySnd :: I → Ar ( I , I ) α → Ar ( I , I ) ( PVec I α) mkAr2DPVecBySnd n ar = array bndsnew

[ ( ( i , j ) , pvec (1,n) [ (k ,ar ! ( i , slo + ( j −slo) ∗ n+(k−1))) | k ← [ 1 .. n] ] ) | ( i , j ) ← range bndsnew]

where

( ( flo , slo ) , ( fhi , shi ) ) = bounds $ ar

Nous définissons également une fonction commune, mkAr2DPVec qui prend un paramètre de plus qui nous sert comme clé dans le choix de soit la fonction mkAr2DPVecByFst ou mkAr2DPVecBySnd :

mkAr2DPVec :: [ Char] → I → Ar ( I , I ) α → Ar ( I , I ) ( PVec I α) mkAr2DPVec how n ar | how == "Fst" = mkAr2DPVecByFst n ar | how == "Snd" = mkAr2DPVecBySnd n ar

4.4.3.3 Dépaquetage des arrays

Nous appelons le dépaquetage d’un array le processus d’obtention d’un array composé des éléments de base à partir d’un array dont les éléments sont des vecteurs paquetés. Il est possible de définir les fonctions de dépaquetage qui auront des fonctionnalités inverses à celles que l’on vient de définir pour le paquetage. Leurs définitions sont intelligibles et nous ne présentons que leurs identificateurs et les signatures de type.

La fonction mkAr1DFromAr1DPVec définit la fonction de dépaquetage pour un array 1D et sa si- gnature de type est la suivante :

mkAr1DFromAr1DPVec:: Ar I ( PVec I α) → Ar I α

Les fonctions mkAr2DFromAr2DPVecByFst et mkAr2DFromAr2DPVecBySnd définissent le dépa- quetage d’un array 2D dans le sens de la première/deuxième coordonnée, respectivement. La fonction mkAr2DFromAr2DPVec les englobe dans une seule qui choisit entre la première/deuxième fonction se- lon la valeur correspondante ”Fst” ou ”Snd” de son premier paramètre textuel (donnée par [Char]). Voici leurs signatures de type :

mkAr2DFromAr2DPVecByFst :: Ar ( I , I ) ( PVec I α) → Ar ( I , I ) α mkAr2DFromAr2DPVecBySnd :: Ar ( I , I ) ( PVec I α) → Ar ( I , I ) α

mkAr2DFromAr2DPVec :: [ Char] → Ar ( I , I ) ( PVec I α) → Ar ( I , I ) α

4.4.4 Sens du parcours, passage d’un array à un flux de données et vice versa