add all schedulers implementation, with an handy way to compile them
This commit is contained in:
parent
0142cc7c14
commit
dab0c3a30e
6 changed files with 425 additions and 10 deletions
26
Makefile
26
Makefile
|
@ -6,11 +6,16 @@ MKDIR = mkdir -p
|
||||||
|
|
||||||
SRC_DIR = src
|
SRC_DIR = src
|
||||||
INC_DIR = includes
|
INC_DIR = includes
|
||||||
|
|
||||||
SOURCES = $(wildcard $(SRC_DIR)/*.c)
|
SOURCES = $(wildcard $(SRC_DIR)/*.c)
|
||||||
OBJETS = $(patsubst %.c,%.o,$(notdir $(SOURCES)))
|
SOURCES_NOSCHED = $(filter-out $(wildcard $(SRC_DIR)/sched-*.c), $(SOURCES))
|
||||||
|
|
||||||
|
ALL_OBJECTS = $(patsubst %.c,%.o,$(notdir $(SOURCES)))
|
||||||
|
OBJECTS = $(patsubst %.c,%.o,$(notdir $(SOURCES_NOSCHED)))
|
||||||
|
|
||||||
CFLAGS = -std=gnu11 -pedantic
|
CFLAGS = -std=gnu11 -pedantic
|
||||||
LDFLAGS =
|
LDFLAGS =
|
||||||
|
SCHED = sched-ws.o
|
||||||
|
|
||||||
EXE = ordonnanceur
|
EXE = ordonnanceur
|
||||||
EXE_EXT = .elf
|
EXE_EXT = .elf
|
||||||
|
@ -30,12 +35,25 @@ debug: CFLAGS += -fanalyzer -fsanitize=undefined -g -Og
|
||||||
debug: LDFLAGS += -fsanitize=undefined
|
debug: LDFLAGS += -fsanitize=undefined
|
||||||
debug: compilation
|
debug: compilation
|
||||||
|
|
||||||
compilation: $(OBJETS)
|
compilation: $(ALL_OBJECTS)
|
||||||
$(CC) -o $(EXE)$(EXE_EXT) $(OBJETS) $(LDFLAGS)
|
$(CC) -o $(EXE)$(EXE_EXT) $(OBJECTS) $(SCHED) $(LDFLAGS)
|
||||||
|
|
||||||
all:
|
all:
|
||||||
release
|
release
|
||||||
|
|
||||||
|
# Specific schedulers
|
||||||
|
threads: SCHED = sched-threads.o
|
||||||
|
threads: release
|
||||||
|
|
||||||
|
stack: SCHED = sched-stack.o
|
||||||
|
stack: release
|
||||||
|
|
||||||
|
random: SCHED = sched-random.o
|
||||||
|
random: release
|
||||||
|
|
||||||
|
ws: SCHED = sched-ws.o
|
||||||
|
ws: release
|
||||||
|
|
||||||
pdf-make:
|
pdf-make:
|
||||||
cd report && \
|
cd report && \
|
||||||
$(MAKE)
|
$(MAKE)
|
||||||
|
@ -45,7 +63,7 @@ pdf-clean:
|
||||||
$(MAKE) clean
|
$(MAKE) clean
|
||||||
|
|
||||||
clean: pdf-clean
|
clean: pdf-clean
|
||||||
$(RM) $(OBJETS) "$(EXE)$(EXE_EXT)" "$(ARCHIVE_NAME).tar"
|
$(RM) *.o "$(EXE)$(EXE_EXT)" "$(ARCHIVE_NAME).tar"
|
||||||
|
|
||||||
archive: pdf-make
|
archive: pdf-make
|
||||||
$(MKDIR) "$(ARCHIVE_NAME)"
|
$(MKDIR) "$(ARCHIVE_NAME)"
|
||||||
|
|
22
README
22
README
|
@ -1,20 +1,34 @@
|
||||||
Projet de programmation système avancée
|
Projet de programmation système avancée
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
Compilation optimisée
|
Compilation optimisée avec ordonnanceur *work-stealing*
|
||||||
---------------------
|
-------------------------------------------------------
|
||||||
|
|
||||||
make
|
make
|
||||||
|
|
||||||
Ce qui créer l'exécutable `ordonnanceur.elf`.
|
Ce qui créer l'exécutable `ordonnanceur.elf`.
|
||||||
|
|
||||||
|
|
||||||
Lancement utilisant tous les cœurs disponibles :
|
Lancement utilisant tous les cœurs disponibles :
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
./ordonnanceur.elf -t 0
|
./ordonnanceur.elf -t 0
|
||||||
|
|
||||||
Info
|
|
||||||
----
|
Autres options
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Il est possible d'utiliser d'autres implémentations d'ordonnanceur en changeant
|
||||||
|
la cible du Makefile.
|
||||||
|
|
||||||
|
- `make threads` : lance juste des threads
|
||||||
|
- `make stack` : utilisation d'une pile
|
||||||
|
- `make random` : idem que stack mais en prenant une tâche aléatoire
|
||||||
|
- `make ws` : work-stealing
|
||||||
|
|
||||||
|
|
||||||
|
Infos
|
||||||
|
-----
|
||||||
|
|
||||||
Le rapport se trouve dans le dossier courant.
|
Le rapport se trouve dans le dossier courant.
|
||||||
|
|
||||||
|
|
179
src/sched-random.c
Normal file
179
src/sched-random.c
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
#include "../includes/sched.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct task_info {
|
||||||
|
void *closure;
|
||||||
|
taskfunc f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scheduler {
|
||||||
|
/* Indicateur de changement d'état */
|
||||||
|
pthread_cond_t cond;
|
||||||
|
|
||||||
|
/* Taille de la pile */
|
||||||
|
int qlen;
|
||||||
|
|
||||||
|
/* Mutex qui protège la structure */
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
/* Nombre de threads instanciés */
|
||||||
|
int nthreads;
|
||||||
|
|
||||||
|
/* Nombre de threads en attente */
|
||||||
|
int nthsleep;
|
||||||
|
|
||||||
|
/* Pile de tâches */
|
||||||
|
struct task_info *tasks;
|
||||||
|
|
||||||
|
/* Position actuelle dans la pile */
|
||||||
|
int top;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Ordonnanceur partagé */
|
||||||
|
static struct scheduler sched;
|
||||||
|
|
||||||
|
/* Lance une tâche de la pile */
|
||||||
|
void *sched_worker(void *);
|
||||||
|
|
||||||
|
int
|
||||||
|
sched_init(int nthreads, int qlen, taskfunc f, void *closure)
|
||||||
|
{
|
||||||
|
if(qlen <= 0) {
|
||||||
|
fprintf(stderr, "qlen must be greater than 0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sched.qlen = qlen;
|
||||||
|
|
||||||
|
if(nthreads < 0) {
|
||||||
|
fprintf(stderr, "nthreads must be greater than 0\n");
|
||||||
|
return -1;
|
||||||
|
} else if(nthreads == 0) {
|
||||||
|
nthreads = sched_default_threads();
|
||||||
|
}
|
||||||
|
sched.nthreads = nthreads;
|
||||||
|
|
||||||
|
if(pthread_mutex_init(&sched.mutex, NULL) != 0) {
|
||||||
|
fprintf(stderr, "Can't init mutex\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pthread_cond_init(&sched.cond, NULL) != 0) {
|
||||||
|
fprintf(stderr, "Can't init condition variable\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sched.top = -1;
|
||||||
|
if((sched.tasks = malloc(qlen * sizeof(struct task_info))) == NULL) {
|
||||||
|
fprintf(stderr, "Can't allocate memory for stack\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise l'aléatoire
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
pthread_t threads[nthreads];
|
||||||
|
for(int i = 0; i < nthreads; ++i) {
|
||||||
|
if(pthread_create(&threads[i], NULL, sched_worker, &sched) != 0) {
|
||||||
|
fprintf(stderr, "Can't create the thread %d\n", i);
|
||||||
|
|
||||||
|
if(i > 0) {
|
||||||
|
fprintf(stderr, ", cancelling already created threads...\n");
|
||||||
|
for(int j = 0; j < i; ++j) {
|
||||||
|
if(pthread_cancel(threads[j]) != 0) {
|
||||||
|
fprintf(stderr, "Can't cancel the thread %d\n", j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sched.tasks);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sched_spawn(f, closure, &sched) < 0) {
|
||||||
|
fprintf(stderr, "Can't create the initial task\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < nthreads; ++i) {
|
||||||
|
if((pthread_join(threads[i], NULL) != 0)) {
|
||||||
|
fprintf(stderr, "Can't wait the thread %d\n", i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sched.tasks);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sched_spawn(taskfunc f, void *closure, struct scheduler *s)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
|
if(s->top + 1 >= s->qlen) {
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
errno = EAGAIN;
|
||||||
|
fprintf(stderr, "Stack is full\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->tasks[++s->top] = (struct task_info){closure, f};
|
||||||
|
|
||||||
|
pthread_cond_signal(&s->cond);
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
sched_worker(void *arg)
|
||||||
|
{
|
||||||
|
struct scheduler *s = (struct scheduler *)arg;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
pthread_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
|
// S'il on a rien à faire
|
||||||
|
if(s->top == -1) {
|
||||||
|
s->nthsleep++;
|
||||||
|
if(s->nthsleep == s->nthreads) {
|
||||||
|
// Signal a tout les threads que il n'y a plus rien à faire
|
||||||
|
// si un thread attend une tâche
|
||||||
|
pthread_cond_broadcast(&s->cond);
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_cond_wait(&s->cond, &s->mutex);
|
||||||
|
s->nthsleep--;
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extrait une tâche aléatoire de la liste
|
||||||
|
int random_index = rand() % (s->top + 1);
|
||||||
|
|
||||||
|
struct task_info echange = s->tasks[random_index];
|
||||||
|
s->tasks[random_index] = s->tasks[s->top];
|
||||||
|
s->tasks[s->top] = echange;
|
||||||
|
|
||||||
|
taskfunc f = s->tasks[s->top].f;
|
||||||
|
void *closure = s->tasks[s->top].closure;
|
||||||
|
s->top--;
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
|
// Exécute la tâche
|
||||||
|
f(closure, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
170
src/sched-stack.c
Normal file
170
src/sched-stack.c
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
#include "../includes/sched.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct task_info {
|
||||||
|
void *closure;
|
||||||
|
taskfunc f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scheduler {
|
||||||
|
/* Indicateur de changement d'état */
|
||||||
|
pthread_cond_t cond;
|
||||||
|
|
||||||
|
/* Taille de la pile */
|
||||||
|
int qlen;
|
||||||
|
|
||||||
|
/* Mutex qui protège la structure */
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
/* Nombre de threads instanciés */
|
||||||
|
int nthreads;
|
||||||
|
|
||||||
|
/* Nombre de threads en attente */
|
||||||
|
int nthsleep;
|
||||||
|
|
||||||
|
/* Pile de tâches */
|
||||||
|
struct task_info *tasks;
|
||||||
|
|
||||||
|
/* Position actuelle dans la pile */
|
||||||
|
int top;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Ordonnanceur partagé */
|
||||||
|
static struct scheduler sched;
|
||||||
|
|
||||||
|
/* Lance une tâche de la pile */
|
||||||
|
void *sched_worker(void *);
|
||||||
|
|
||||||
|
int
|
||||||
|
sched_init(int nthreads, int qlen, taskfunc f, void *closure)
|
||||||
|
{
|
||||||
|
if(qlen <= 0) {
|
||||||
|
fprintf(stderr, "qlen must be greater than 0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sched.qlen = qlen;
|
||||||
|
|
||||||
|
if(nthreads < 0) {
|
||||||
|
fprintf(stderr, "nthreads must be greater than 0\n");
|
||||||
|
return -1;
|
||||||
|
} else if(nthreads == 0) {
|
||||||
|
nthreads = sched_default_threads();
|
||||||
|
}
|
||||||
|
sched.nthreads = nthreads;
|
||||||
|
|
||||||
|
if(pthread_mutex_init(&sched.mutex, NULL) != 0) {
|
||||||
|
fprintf(stderr, "Can't init mutex\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pthread_cond_init(&sched.cond, NULL) != 0) {
|
||||||
|
fprintf(stderr, "Can't init condition variable\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sched.top = -1;
|
||||||
|
if((sched.tasks = malloc(qlen * sizeof(struct task_info))) == NULL) {
|
||||||
|
fprintf(stderr, "Can't allocate memory for stack\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t threads[nthreads];
|
||||||
|
for(int i = 0; i < nthreads; ++i) {
|
||||||
|
if(pthread_create(&threads[i], NULL, sched_worker, &sched) != 0) {
|
||||||
|
fprintf(stderr, "Can't create the thread %d\n", i);
|
||||||
|
|
||||||
|
if(i > 0) {
|
||||||
|
fprintf(stderr, ", cancelling already created threads...\n");
|
||||||
|
for(int j = 0; j < i; ++j) {
|
||||||
|
if(pthread_cancel(threads[j]) != 0) {
|
||||||
|
fprintf(stderr, "Can't cancel the thread %d\n", j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sched.tasks);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sched_spawn(f, closure, &sched) < 0) {
|
||||||
|
fprintf(stderr, "Can't create the initial task\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < nthreads; ++i) {
|
||||||
|
if((pthread_join(threads[i], NULL) != 0)) {
|
||||||
|
fprintf(stderr, "Can't wait the thread %d\n", i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sched.tasks);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sched_spawn(taskfunc f, void *closure, struct scheduler *s)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
|
if(s->top + 1 >= s->qlen) {
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
errno = EAGAIN;
|
||||||
|
fprintf(stderr, "Stack is full\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->tasks[++s->top] = (struct task_info){closure, f};
|
||||||
|
|
||||||
|
pthread_cond_signal(&s->cond);
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
sched_worker(void *arg)
|
||||||
|
{
|
||||||
|
struct scheduler *s = (struct scheduler *)arg;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
pthread_mutex_lock(&s->mutex);
|
||||||
|
|
||||||
|
// S'il on a rien à faire
|
||||||
|
if(s->top == -1) {
|
||||||
|
s->nthsleep++;
|
||||||
|
if(s->nthsleep == s->nthreads) {
|
||||||
|
// Signal a tout les threads que il n'y a plus rien à faire
|
||||||
|
// si un thread attend une tâche
|
||||||
|
pthread_cond_broadcast(&s->cond);
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_cond_wait(&s->cond, &s->mutex);
|
||||||
|
s->nthsleep--;
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extrait la tâche de la pile
|
||||||
|
taskfunc f = s->tasks[s->top].f;
|
||||||
|
void *closure = s->tasks[s->top].closure;
|
||||||
|
s->top--;
|
||||||
|
pthread_mutex_unlock(&s->mutex);
|
||||||
|
|
||||||
|
// Exécute la tâche
|
||||||
|
f(closure, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
34
src/sched-threads.c
Normal file
34
src/sched-threads.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#include "../includes/sched.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
sched_init(int nthreads, int qlen, taskfunc f, void *closure)
|
||||||
|
{
|
||||||
|
sched_spawn(f, closure, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sched_spawn(taskfunc f, void *closure, struct scheduler *s)
|
||||||
|
{
|
||||||
|
pthread_t thread;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
// Création d'un thread pour la tâche
|
||||||
|
if((err = pthread_create(&thread, NULL, (void *(*)(void *))f, closure)) !=
|
||||||
|
0) {
|
||||||
|
fprintf(stderr, "pthread_create error %d\n", errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attend la fin du thread
|
||||||
|
if((err = pthread_join(thread, NULL)) != 0) {
|
||||||
|
fprintf(stderr, "pthread_join error %d\n", errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in a new issue