thread
This commit is contained in:
parent
4d54779703
commit
67f4b827c7
@ -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)
|
#### Semaine 5 (30/09 - 04/10)
|
||||||
[Tubes, redirections](cours/pipe.pdf), [td5](td/td5/td5.pdf), [tp5](tp/tp5).
|
[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).
|
||||||
|
|
||||||
|
|
||||||
|
BIN
cours/thread.pdf
Normal file
BIN
cours/thread.pdf
Normal file
Binary file not shown.
180
tp/tp6/README.md
Normal file
180
tp/tp6/README.md
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
# TP : Threads
|
||||||
|
|
||||||
|
#### Ex1
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#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<N;i++){
|
||||||
|
y[i]=0;
|
||||||
|
for(j=0;j<P;j++)
|
||||||
|
y[i] += A[i][j]*x[j]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Écrire un programme qui prend en argument un entier `n`, et qui crée autant de threads pour
|
||||||
|
le cacul du produit. Chaque thread recevra en argument l'intervalle des indices `i in [start,end]` pour lequels il doit
|
||||||
|
effectuer le calcul `y[i]`. Pour le test, utilisez des matrices de type `int` en variables globales préalablement
|
||||||
|
initialisées aléatoirement.
|
||||||
|
|
||||||
|
2. Remplissez le tableau suivant :
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr><td rowspan=3>nb Threads</td><td colspan=3>Dimensions</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td>8000000x8</td><td>8000x8000</td><td>8x8000000</td></tr>
|
||||||
|
<tr><td>temps</td><td>temps</td><td>temps</td></tr>
|
||||||
|
<tr><td>1</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td>2</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td>4</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td>8</td><td></td><td></td><td></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
27
tp/tp6/src/ex1.c
Normal file
27
tp/tp6/src/ex1.c
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
10
tp/tp6/src/stack/Makefile
Normal file
10
tp/tp6/src/stack/Makefile
Normal file
@ -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
|
21
tp/tp6/src/stack/main.c
Normal file
21
tp/tp6/src/stack/main.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#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;
|
||||||
|
}
|
32
tp/tp6/src/stack/stack.c
Normal file
32
tp/tp6/src/stack/stack.c
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "stack.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
12
tp/tp6/src/stack/stack.h
Normal file
12
tp/tp6/src/stack/stack.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user