Fireball

Node Level Parallelism

Un petit cours de comm ?

Adrien Merlini & Serge Guelton

Systèmes distribués : ils sont partout

Data Center, où, quand, comment, pourquoi

https://www.google.com/about/datacenters

À toutes les échelles

Top 500

https://www.top500.org/

June 1993: Rpeak 131 GFlops, 1,024 Cores, USA June 2003: RPeak 40,960 GFlops, 5,120 Cores, Japan June 2013: RPeak 54,902 GFlops, 3,120,000 Cores, China June 2020: RPeak 513,584 GFlops, 7,299,072 Cores, Japan

Green 500

https://www.top500.org/lists/green500/

June 2013: 3208.8 MFlops/watts June 2020: 21.108 GFlops/watts, 393 in TOP500

SETI@Home (et ses successeurs)

https://setiathome.berkeley.edu/

> analyse observationnelle cherchant à détecter de la vie intelligente non terrestre Depuis 1999 !

Objectif officieux: prouver la viabilité d'une grille distribuée à base de machines sur étagères

successeur : https://boinc.berkeley.edu/

L'ancètre : MPI

https://www.mpi-forum.org/docs/

> Message Passing Interface

Tellement important que les constructeurs fournissent leur propre versions (e.g. Intel, IBM…)

Une tendance : Celery

https://docs.celeryproject.org/

> Distributed Task Queue

L'ordonnanceur : Slurm

https://www.schedmd.com/

> workload manager

Le modeste : Make

https://www.gnu.org/software/make/

> build automation

Une question de grain

Surcouts possibles :

Qui s'ajoutent au temps d'exécution de la tâche…

De la taille des tuyaux

Aux débuts de l'informatique, les processeurs étaient lents :

→ on optimise le nombre d'instruction, pas les transferts mémoires

au 21ème siècle, les processeurs sont rapides :

→ on optimise les accès mémoires

Mais tout dépend du système ! MPI ≠ OpenMP

Communication entre architectures différentes

Que se passe-t-il quand un processeur arm32 bit envoie un tableau de int à un processeur amd64 ?

Écueils possibles :

MPI API : compilation et exécution

MPI API : contexte

Création et fermeture de contexte :

Les autres appels MPI ne sont valides qu'entre ces deux appels

MPI API : communicateurs

communicateur = ensemble de processus + contexte de comm prédéfini: MPI_Comm MPI_COMM_WORLD

MPI API : envoi d'un message

int MPI_Send(void *buf, // données à envoyer
             int count, // nombre d'élément données
             MPI_Datatype datatype, // type d'un élément
             int dest, // étiquette du destinataire
             int tag, // identifiant du message
             MPI_Comm comm,  // communicateur choisi
);

MPI API : réception d'un message

int MPI_Recv(void *buf,  // adresse de réception
             int count,  // nombre max. d'éléments
             MPI_Datatype datatype, // type d'un élément
             int source,  // rang de la source
             int tag, // étiquette du message attendu
             MPI_Comm comm, // communicateur choisi
             MPI_Status *status  // descripteur de la com.
);

MPI API : statut d'un message

struct MPIStatus{
    int MPISOURCE ; // rang de la source, cf. MPIANYSOURCE
    int MPITAG ; //  ́étiquette du message recu, cf. MPIANYTAG
    int MPIERROR ; // code d'erreur
};

int MPI_Get_count(MPI_Status *status,  // statut à inspecter
                  MPI_Datatype datatype,  // type des éléments transférés
                  int *count  // nombre d'éléments reçus
);

MPI API : inspection d'un message

int MPI_Probe(int source, // rang de la source
              int tag, // étiquette du message attendu
              MPI_Comm comm, // communicateur choisi
              MPI_Status *status  // descripteur de la com.
);

Bloquant, pratique pour dimensionner un tampon avant un MPI_Recv.

MPI API : transferts non bloquants

int MPI_Isend(..., // mêmes arguments que MPI_Send
              MPI_Request* req // requète interrogeable
);

int MPI_Wait(MPI_Request* req, // requète à interroger
             MPI_Status* status // status de la requète
);

int MPI_Test(MPI_Request *req, // requète à interroger
             int *flag, // positionné à 0 si envoi non terminé
             MPI_Status *status  // statut à maj
);

MPI API : broadcasting

int MPI_Bcast(void *buffer, // cf. MPI_Send / MPI_Recv
              int count, // cf. MPI_Send / MPI_Recv
              MPI_Datatype datatype, // cf. MPI_Send / MPI_Recv
              int root, // rang de l'envoyeur
              MPI_Comm comm // cf. MPI_Send
);

[!] les nœuds de rang ≠ root reçoivent les données à l'issue de cet appel

MPI API: scatter / gather

                SCATTER            GATHER

A:               +-- 0         0 -- +
B:               +-- 1         1 -- +
C:  [0,1,2,3] > -|                  | - > [0, 1, 2, 3]
D:               +-- 2         2 -- +
E:               +-- 3         3 -- +

MPI API: scatter / gather

 int MPI_Scatter(
     // description des données envoyées aux autres nœuds
     const void *sendbuf, int sendcount, MPI_Datatype sendtype,
     // description desdonnées reçues par chaque autre nœud
     void *recvbuf, int recvcount, MPI_Datatype recvtype,
     int root, // rang de l'envoyeur
     MPI_Comm comm // …
 );
[!] recvcount * comm_size = sendcount

// idem
int MPI_Gather(
    const void *sendbuf, int sendcount, MPI_Datatype sendtype,
    void *recvbuf, int recvcount, MPI_Datatype recvtype,
    int root,
    MPI_Comm comm)
[!] recvcount = sendcount * comm_size
1