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
|
||||
INC_DIR = includes
|
||||
|
||||
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
|
||||
LDFLAGS =
|
||||
SCHED = sched-ws.o
|
||||
|
||||
EXE = ordonnanceur
|
||||
EXE_EXT = .elf
|
||||
|
@ -30,12 +35,25 @@ debug: CFLAGS += -fanalyzer -fsanitize=undefined -g -Og
|
|||
debug: LDFLAGS += -fsanitize=undefined
|
||||
debug: compilation
|
||||
|
||||
compilation: $(OBJETS)
|
||||
$(CC) -o $(EXE)$(EXE_EXT) $(OBJETS) $(LDFLAGS)
|
||||
compilation: $(ALL_OBJECTS)
|
||||
$(CC) -o $(EXE)$(EXE_EXT) $(OBJECTS) $(SCHED) $(LDFLAGS)
|
||||
|
||||
all:
|
||||
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:
|
||||
cd report && \
|
||||
$(MAKE)
|
||||
|
@ -45,7 +63,7 @@ pdf-clean:
|
|||
$(MAKE) clean
|
||||
|
||||
clean: pdf-clean
|
||||
$(RM) $(OBJETS) "$(EXE)$(EXE_EXT)" "$(ARCHIVE_NAME).tar"
|
||||
$(RM) *.o "$(EXE)$(EXE_EXT)" "$(ARCHIVE_NAME).tar"
|
||||
|
||||
archive: pdf-make
|
||||
$(MKDIR) "$(ARCHIVE_NAME)"
|
||||
|
|
22
README
22
README
|
@ -1,20 +1,34 @@
|
|||
Projet de programmation système avancée
|
||||
=======================================
|
||||
|
||||
Compilation optimisée
|
||||
---------------------
|
||||
Compilation optimisée avec ordonnanceur *work-stealing*
|
||||
-------------------------------------------------------
|
||||
|
||||
make
|
||||
|
||||
Ce qui créer l'exécutable `ordonnanceur.elf`.
|
||||
|
||||
|
||||
Lancement utilisant tous les cœurs disponibles :
|
||||
------------------------------------------------
|
||||
|
||||
./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.
|
||||
|
||||
|
|
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