ajout tp- et cm thread
This commit is contained in:
@@ -34,5 +34,8 @@ Rappels sur la gestion de la [mémoire](cours/memoire.pdf), [td1](td/td1/td1.pdf
|
||||
### Semaine 5 (29/09 - 03/10)
|
||||
[Tubes, redirections](cours/pipe.pdf), [td5](td/td5/td5.pdf), [tp5](tp/tp5).
|
||||
|
||||
#### Semaine 6 (06/10 - 10/10)
|
||||
[API threads posix](cours/thread.pdf), [tp6](tp/tp6).
|
||||
|
||||
|
||||
|
||||
|
||||
160
tp/tp6/README.md
Normal file
160
tp/tp6/README.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# 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
|
||||
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_MONOTONIC, &tv);
|
||||
return tv.tv_sec + tv.tv_nsec * 1.0e-9;
|
||||
}
|
||||
```
|
||||
#### Ex22
|
||||
On reprend l'exercice d'un tp précédent qui calcule une approximation du nombre pi en simulant des lancers de flechettes
|
||||
sur un disque.
|
||||
|
||||
Un lancer consiste à tirer aléatoirement la position `(x,y)` de la flechette dans le quart supérieur droit du disque `[0,1]x[0,1]`.
|
||||
On comptabilise le nombre de lancers à l'intérieur du disque (`x^2+y^2 <= 1`).
|
||||
|
||||
Paralléliser le [code](src/pi.c) en créant pour la simulation plusieurs threads (passé à la ligne de commande).
|
||||
|
||||
Remarque : dans le code fournit, on utilise la fonction `rand_r`, et pas `rand`. Cette dernière n'est pas réentrante (état global caché).
|
||||
|
||||
#### Ex3
|
||||
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.
|
||||
|
||||
|
||||
#### Ex4 (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.
|
||||
|
||||
|
||||
|
||||
BIN
tp/tp6/src/a.out
Executable file
BIN
tp/tp6/src/a.out
Executable file
Binary file not shown.
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;
|
||||
}
|
||||
|
||||
50
tp/tp6/src/pi.c
Normal file
50
tp/tp6/src/pi.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
void* nbDansCercleSeq( void* nbLancers )
|
||||
{
|
||||
long nb = 0;
|
||||
unsigned int seed;
|
||||
for( long i = 0; i < (long) nbLancers; i++ ) {
|
||||
double x = (double) rand_r(&seed) / RAND_MAX;
|
||||
double y = (double) rand_r(&seed) / RAND_MAX;
|
||||
if ( x * x + y * y <= 1.0 ) {
|
||||
nb += 1;
|
||||
}
|
||||
}
|
||||
pthread_exit( (void*) nb ); // <--- on triche ici !!!
|
||||
}
|
||||
|
||||
double evaluerPi( long nbLancers , long nbThreads )
|
||||
{
|
||||
long nbTotalDansCercle = 0;
|
||||
|
||||
// TODO
|
||||
//
|
||||
return 4.0 * nbTotalDansCercle / nbLancers;
|
||||
}
|
||||
|
||||
|
||||
static inline double tstamp(void)
|
||||
{
|
||||
struct timespec tv;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tv);
|
||||
return tv.tv_sec + tv.tv_nsec * 1.0e-9;
|
||||
}
|
||||
|
||||
int main( int argc , char *argv[] )
|
||||
{
|
||||
assert( argc >= 3 );
|
||||
long nbLancers = strtol(argv [1], NULL, 0);
|
||||
assert( nbLancers > 0 );
|
||||
long nbThreads = strtol(argv [2], NULL, 0);
|
||||
assert( nbThreads > 0 );
|
||||
double t1,t2;
|
||||
t1 = tstamp();
|
||||
double pi = evaluerPi(nbLancers, nbThreads);
|
||||
t2 = tstamp();
|
||||
printf( "pi = %f time = %f\n",pi, t2-t1);
|
||||
return 0;
|
||||
}
|
||||
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
|
||||
Reference in New Issue
Block a user