Compare commits
7 Commits
a677d98c2c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b6c3b42361 | |||
| 126b0890f1 | |||
| 950c3bb003 | |||
| 3473b836b8 | |||
| f83540153d | |||
| cf2109a434 | |||
| 348e3b73d8 |
@@ -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)
|
### Semaine 5 (29/09 - 03/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 (06/10 - 10/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.
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 `drand48_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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
30
tp/tp6/correc_gr34/ex1.c
Normal file
30
tp/tp6/correc_gr34/ex1.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define NUM_THREADS 16
|
||||||
|
|
||||||
|
void *thread(void *thread_id) {
|
||||||
|
long id = ((long ) thread_id);
|
||||||
|
printf("Hello from thread %ld\n", id);
|
||||||
|
|
||||||
|
return (void*)(2*id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
pthread_t threads[NUM_THREADS];
|
||||||
|
int t[NUM_THREADS];
|
||||||
|
long res;
|
||||||
|
for (long i = 0; i < NUM_THREADS; i++){
|
||||||
|
// t[i] = i;
|
||||||
|
assert( pthread_create(&threads[i], NULL, thread, (void*)i) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_THREADS; i++){
|
||||||
|
assert( pthread_join(threads[i],(void *)&res) == 0);
|
||||||
|
printf("%ld\n",res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
60
tp/tp6/correc_gr34/sum.c
Normal file
60
tp/tp6/correc_gr34/sum.c
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
static inline double tstamp(void)
|
||||||
|
{
|
||||||
|
struct timespec tv;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &tv);
|
||||||
|
return tv.tv_sec + tv.tv_nsec * 1.0e-9;
|
||||||
|
}
|
||||||
|
typedef struct arg {
|
||||||
|
long start;
|
||||||
|
long end;
|
||||||
|
long step;
|
||||||
|
long S;
|
||||||
|
} arg;
|
||||||
|
|
||||||
|
void* slice(void* a) {
|
||||||
|
long start = ((arg*)a)->start;
|
||||||
|
long end = ((arg*)a)->end;
|
||||||
|
long step= ((arg*)a)->step;
|
||||||
|
long S = 0;
|
||||||
|
for(long i = start; i <= end; i += step)
|
||||||
|
S += i;
|
||||||
|
((arg*)a)->S = S;
|
||||||
|
return NULL;
|
||||||
|
//return (void*)S;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
long N = strtol(argv[1], NULL,0);
|
||||||
|
long M = strtol(argv[2], NULL,0);
|
||||||
|
long S = 0, ret;
|
||||||
|
double t1,t2;
|
||||||
|
pthread_t* threads = (pthread_t*)calloc(M,sizeof(pthread_t));
|
||||||
|
arg* args = (arg*)calloc(M,sizeof(arg));
|
||||||
|
t1 = tstamp();
|
||||||
|
for (long i = 0; i < M; i++){
|
||||||
|
|
||||||
|
args[i].start = i + 1;
|
||||||
|
args[i].end = N;
|
||||||
|
args[i].step = M;
|
||||||
|
|
||||||
|
assert( pthread_create(&threads[i], NULL, slice, (void*)(args + i)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < M; i++){
|
||||||
|
// assert( pthread_join(threads[i], (void*)&ret) == 0);
|
||||||
|
assert( pthread_join(threads[i], NULL) == 0);
|
||||||
|
S += args[i].S;
|
||||||
|
}
|
||||||
|
t2 = tstamp();
|
||||||
|
assert( S == N*(N+1)/2 );
|
||||||
|
printf("S = %ld time = %lf\n",S,t2 - t1);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
55
tp/tp6/src/pi.c
Normal file
55
tp/tp6/src/pi.c
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void* nbDansCercleSeq( void* nbLancers )
|
||||||
|
{
|
||||||
|
long nb = 0;
|
||||||
|
struct drand48_data bf;
|
||||||
|
double x,y;
|
||||||
|
srand48_r(pthread_self(),&bf); // initialisation de la graine
|
||||||
|
// avec le numero du thread
|
||||||
|
|
||||||
|
for( long i = 0; i < (long) nbLancers; i++ ) {
|
||||||
|
drand48_r(&bf,&x);
|
||||||
|
drand48_r(&bf,&y);
|
||||||
|
|
||||||
|
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