diff --git a/README.md b/README.md index fd75bf6..d1e5076 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,7 @@ Rappels sur la gestion de la [mémoire](cours/memoire.pdf), [td1](td/td1/td1.pdf #### Semaine 5 (30/09 - 04/10) [Tubes, redirections](cours/pipe.pdf), [td5](td/td5/td5.pdf), [tp5](tp/tp5). +#### Semaine 6 (07/10 - 11/10) +[API threads posix](cours/thread.pdf), [tp6](tp/tp6). + diff --git a/cours/thread.pdf b/cours/thread.pdf new file mode 100644 index 0000000..d053a7d Binary files /dev/null and b/cours/thread.pdf differ diff --git a/tp/tp6/README.md b/tp/tp6/README.md new file mode 100644 index 0000000..fac584c --- /dev/null +++ b/tp/tp6/README.md @@ -0,0 +1,180 @@ +# TP : Threads + +#### Ex1 +```c +#include +#include +#include +#include + +#define NUM_THREADS 16 + +void *thread(void *thread_id) { + int id = *((int *) thread_id); + printf("Hello from thread %d\n", id); + + return NULL; +} + +int main() { + pthread_t threads[NUM_THREADS]; + for (int i = 0; i < NUM_THREADS; i++) + assert( pthread_create(&threads[i], NULL, thread, &i) == 0); + + for (int i = 0; i < NUM_THREADS; i++) + assert( pthread_join(threads[i], NULL) == 0); + + return EXIT_SUCCESS; +} +``` + +1. Est-ce que l’exécution de ce programme est correcte? Vous pouvez vous en assurer en l’exécutant plusieurs fois. +2. Si vous pensez (et avez constaté) que ce n’est pas le cas, expliquez pourquoi. +3. Modifiez le code pour qu’il donne le résultat attendu. + +#### Ex2 +Ecrire un programme max.c qui accepte la ligne de commande : + +``` +./max n1 n2 n3 n4 n5 n6 n7 n8 +``` + +et qui calcule le maximum des entiers (n1, n2, ..n8) en utlisant le schéma suivant (tournoi) : + +``` + n1 n2 n3 n4 n5 n6 n7 n8 + 2 5 17 26 -3 12 1 3 <--- 4 duels (threads) + \ / \ / \ / \ / + \ / \ / \ / \ / + 5 26 12 3 <--- 2 duels (threads) + \ / \ / + \ / \ / + 26 12 <--- 1 duel (thread) + \ / + \ / + \ / + \ / + 26 + +``` + + - Chaque duel est un thread. + - Le thread prinicpal organise le tournoi, et proclame le vainqueur. + + + +#### Ex3 +On veut écrire un programme calculant la somme des entiers +de `1` à `N` à l’aide de `M` threads. Chaque thread calculera la somme +d’un sous-ensemble de ces entiers et la somme globale sera obtenue en +calculant la somme des résultats intermédiaires de chaque thread. + +Les entiers sont répartis uniformément entre les threads comme suit (exemple avec 3 threads) : + +Thread 1 : 1, 4, 7, ... +Thread 2 : 2, 5, 8, ... +Thread 3 : 3, 6, 9, ... + + +Le programme doit lancer `M` threads, attendre qu’ils se terminent, faire la somme des résultats +intermédiaires et afficher le résultat. Les valeurs `N` et `M` seront passées en ligne de commande. + +Il est important que le programme respecte les points suivants : + +- L’implémentation ne doit utiliser aucune variable globale. +- Le travail à effectuer pour chaque thread créé doit être aussi équitable que possible, quelles + que soient les valeurs `N` et `M` choisies par l’utilisateur (ex : N=20, M=8). +- Évitez d’utiliser un tableau pour contenir les valeurs à additionner. +- Réaliser un test de validation automatiquement du résultat obtenu (vous devez connaître le résultat !). + + +Comparez le temps d'éxecution en fonction du nombre de threads. + + + +```c +/* pour "mesurer" le temps */ +static inline double tstamp(void) +{ + struct timespec tv; + clock_gettime(CLOCK_REALTIME, &tv); + return tv.tv_sec + tv.tv_nsec * 1.0e-9; +} +``` + +#### Ex4 +On souhaite écrire une petite librairie implantant une pile d'entiers. Sa taille sera statique, et determinée au moment +de sa création. Voici l'interface : + +```c +stack_t * stack_create(int max_size); +``` +crée une pile de taille `max_size` + +```c +int stack_destroy(stack_t *s); +``` + +détruit la pile + +```c +int stack_push(stack_t *s, int val); +``` + +empile la valeur `val` renvoie 1 en cas de succès, 0 sinon. + +```c +int stack_pop(stack_t *s,int *val); +``` + +dépile dans `*val`. renvoie 1 en cas de succès, 0 sinon. + + +( Décider ce que contiendra la structure stack_t définissant un objet de type pile et penser à insérer des assertions dans le code aux endroits nécessaires.) + +1. Testez votre implantation avec une utilisation monothreadé. +2. Testez votre implantation avec ube utilisation multithreadé (une même pile utlisé par plusieurs threads). Cela fonctionne-t-il ? +3. Ajoutez des primitives d'exclusions mutuelles afin de garantir un comportement cohérent et déterministe dans le cas d’une exécution multi-threadée. + + +#### Ex5 (Mutltiplication matrice/vecteur, problème du false sharing) +Le but de l'exercice est de parallèliser le calcul du produit matrice/vecteur. + +Pour rappel, le produit d'une matrice `A[N][P]` par un vecteur `x[P]` donne un vecteur +`y[N]` par : + +```c +for(i=0;i + + nb ThreadsDimensions + + 8000000x88000x80008x8000000 + tempstempstemps + 1 + 2 + 4 + 8 + + + + Que constatez-vous pour la dernière colonne ? + [ chaque processeur utilise un cache de premier niveau (L1) dont les lignes font + 64 octets. Les composantes du vecteur `y` ne sont pas accèder de manière concurrente. Mais il se peut qu'une ligne d'un cache contienne une composante écrite par un autre thread dans un autre cache rendant la ligne ```dirty``` ] + + Modifiez le code pour remédier au problème. + + + diff --git a/tp/tp6/src/ex1.c b/tp/tp6/src/ex1.c new file mode 100644 index 0000000..3500d87 --- /dev/null +++ b/tp/tp6/src/ex1.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +#define NUM_THREADS 16 + +void *thread(void *thread_id) +{ + int id = *((int *) thread_id); + printf("Hello from thread %d\n", id); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + pthread_t threads[NUM_THREADS]; + for (int i = 0; i < NUM_THREADS; i++) + assert( pthread_create(&threads[i], NULL, thread, &i) == 0); + + for (int i = 0; i < NUM_THREADS; i++) + assert( pthread_join(threads[i], NULL) == 0); + + return EXIT_SUCCESS; +} + diff --git a/tp/tp6/src/stack/Makefile b/tp/tp6/src/stack/Makefile new file mode 100644 index 0000000..28447ff --- /dev/null +++ b/tp/tp6/src/stack/Makefile @@ -0,0 +1,10 @@ +stack.o : stack.c + gcc -c stack.c + +main.o : main.c stack.h + gcc -c main.c + +main : main.o stack.o + gcc -o main main.o stack.o + +all : main diff --git a/tp/tp6/src/stack/main.c b/tp/tp6/src/stack/main.c new file mode 100644 index 0000000..514a427 --- /dev/null +++ b/tp/tp6/src/stack/main.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include "stack.h" + + + +int main(int argc, char *argv[]) +{ + + int val; + stack_t * s=stack_create(20); + + for(int i=0;i<20; i++) + stack_push(s,i); + + for(int i=0;i<20; i++) + stack_pop(s,&val); + + return 0; +} diff --git a/tp/tp6/src/stack/stack.c b/tp/tp6/src/stack/stack.c new file mode 100644 index 0000000..d1cc620 --- /dev/null +++ b/tp/tp6/src/stack/stack.c @@ -0,0 +1,32 @@ +#include "stack.h" +#include +#include + +struct stack_t { + /* + votre implantation + + */ +}; + +stack_t * stack_create( int max_size) +{ + +} + + +int stack_destroy(stack_t * s) +{ + +} + +int stack_push(stack_t *s,int val) +{ + +} + +int stack_pop(stack_t *s,int * val) +{ + +} + diff --git a/tp/tp6/src/stack/stack.h b/tp/tp6/src/stack/stack.h new file mode 100644 index 0000000..24768ec --- /dev/null +++ b/tp/tp6/src/stack/stack.h @@ -0,0 +1,12 @@ +#ifndef _STACK_INT_H +#define _STACK_INT_H + +typedef struct stack_t stack_t; + +stack_t * stack_create(int max_size); +int stack_destroy(stack_t *s); +int stack_push(stack_t *s, int val); +int stack_pop(stack_t *s,int *val); + + +#endif