reformat, following initial convention

This commit is contained in:
Mylloon 2024-04-19 15:33:03 +02:00
parent f95dcaf491
commit 6b891cf338
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
5 changed files with 266 additions and 241 deletions

5
.clang-format Normal file
View file

@ -0,0 +1,5 @@
IndentWidth: 4
InsertBraces: true
BreakBeforeBraces: Linux
AlwaysBreakAfterReturnType: TopLevelDefinitions
SpaceBeforeParens: Never

View file

@ -10,29 +10,31 @@ struct scheduler;
typedef void (*taskfunc)(void *, struct scheduler *); typedef void (*taskfunc)(void *, struct scheduler *);
typedef struct task_info { typedef struct task_info {
taskfunc f; taskfunc f;
void *closure; void *closure;
} taskinfo; } taskinfo;
struct scheduler { struct scheduler {
/* Mutex qui protège la structure */ /* Mutex qui protège la structure */
pthread_mutex_t mutex; pthread_mutex_t mutex;
/* Indicateur de changement d'état */ /* Indicateur de changement d'état */
pthread_cond_t cond; pthread_cond_t cond;
/* Position actuelle dans la pile */ /* Position actuelle dans la pile */
int top; int top;
/* Tâches */ /* Tâches */
taskinfo tasks[MAX_TASKS]; taskinfo tasks[MAX_TASKS];
/* Si tout est terminé */ /* Si tout est terminé */
int exit; int exit;
}; };
static inline int sched_default_threads(void) { static inline int
return sysconf(_SC_NPROCESSORS_ONLN); sched_default_threads(void)
{
return sysconf(_SC_NPROCESSORS_ONLN);
} }
/* Lance l'ordonnanceur /* Lance l'ordonnanceur

View file

@ -1,5 +1,7 @@
#include "../includes/quicksort.h" #include "../includes/quicksort.h"
int main(int argc, char *argv[]) { int
return benchmark(argc, argv); main(int argc, char *argv[])
{
return benchmark(argc, argv);
} }

View file

@ -1,151 +1,161 @@
#include "../includes/quicksort.h" #include "../includes/quicksort.h"
int partition(int *a, int lo, int hi) { int
int pivot = a[lo]; partition(int *a, int lo, int hi)
int i = lo - 1; {
int j = hi + 1; int pivot = a[lo];
int t; int i = lo - 1;
while (1) { int j = hi + 1;
do { int t;
i++; while(1) {
} while (a[i] < pivot); do {
i++;
} while(a[i] < pivot);
do { do {
j--; j--;
} while (a[j] > pivot); } while(a[j] > pivot);
if (i >= j) { if(i >= j) {
return j; return j;
}
t = a[i];
a[i] = a[j];
a[j] = t;
} }
t = a[i];
a[i] = a[j];
a[j] = t;
}
} }
struct quicksort_args { struct quicksort_args {
int *a; int *a;
int lo, hi; int lo, hi;
}; };
struct quicksort_args *new_args(int *a, int lo, int hi) { struct quicksort_args *
struct quicksort_args *args = malloc(sizeof(struct quicksort_args)); new_args(int *a, int lo, int hi)
if (args == NULL) { {
return NULL; struct quicksort_args *args = malloc(sizeof(struct quicksort_args));
} if(args == NULL) {
return NULL;
args->a = a;
args->lo = lo;
args->hi = hi;
return args;
}
void quicksort_serial(int *a, int lo, int hi) {
int p;
if (lo >= hi) {
return;
}
p = partition(a, lo, hi);
quicksort_serial(a, lo, p);
quicksort_serial(a, p + 1, hi);
}
void quicksort(void *closure, struct scheduler *s) {
struct quicksort_args *args = (struct quicksort_args *)closure;
int *a = args->a;
int lo = args->lo;
int hi = args->hi;
int p;
int rc;
free(closure);
if (lo >= hi) {
return;
}
if (hi - lo <= 128) {
quicksort_serial(a, lo, hi);
return;
}
p = partition(a, lo, hi);
rc = sched_spawn(quicksort, new_args(a, lo, p), s);
assert(rc >= 0);
rc = sched_spawn(quicksort, new_args(a, p + 1, hi), s);
assert(rc >= 0);
}
int benchmark(int argc, char **argv) {
int *a;
struct timespec begin, end;
double delay;
int rc;
int n = 10 * 1024 * 1024;
int nthreads = -1;
int serial = 0;
while (1) {
int opt = getopt(argc, argv, "sn:t:");
if (opt < 0) {
break;
} }
switch (opt) {
case 's': args->a = a;
serial = 1; args->lo = lo;
break; args->hi = hi;
case 'n': return args;
n = atoi(optarg); }
break;
case 't': void
nthreads = atoi(optarg); quicksort_serial(int *a, int lo, int hi)
break; {
default: int p;
goto usage;
if(lo >= hi) {
return;
} }
}
if (n <= 0) { p = partition(a, lo, hi);
goto usage; quicksort_serial(a, lo, p);
} quicksort_serial(a, p + 1, hi);
if (nthreads < 0 && !serial) { }
goto usage;
}
a = malloc(n * sizeof(int)); void
quicksort(void *closure, struct scheduler *s)
{
struct quicksort_args *args = (struct quicksort_args *)closure;
int *a = args->a;
int lo = args->lo;
int hi = args->hi;
int p;
int rc;
unsigned long long s = 0; free(closure);
for (int i = 0; i < n; i++) {
s = s * 6364136223846793005ULL + 1442695040888963407;
a[i] = (int)((s >> 33) & 0x7FFFFFFF);
}
clock_gettime(CLOCK_MONOTONIC, &begin); if(lo >= hi) {
return;
}
if (serial) { if(hi - lo <= 128) {
quicksort_serial(a, 0, n - 1); quicksort_serial(a, lo, hi);
} else { return;
rc = }
sched_init(nthreads, (n + 127) / 128, quicksort, new_args(a, 0, n - 1));
p = partition(a, lo, hi);
rc = sched_spawn(quicksort, new_args(a, lo, p), s);
assert(rc >= 0); assert(rc >= 0);
} rc = sched_spawn(quicksort, new_args(a, p + 1, hi), s);
assert(rc >= 0);
}
clock_gettime(CLOCK_MONOTONIC, &end); int
delay = end.tv_sec + end.tv_nsec / 1000000000.0 - benchmark(int argc, char **argv)
(begin.tv_sec + begin.tv_nsec / 1000000000.0); {
printf("Done in %lf seconds.\n", delay); int *a;
struct timespec begin, end;
double delay;
int rc;
int n = 10 * 1024 * 1024;
int nthreads = -1;
int serial = 0;
for (int i = 0; i < n - 1; i++) { while(1) {
assert(a[i] <= a[i + 1]); int opt = getopt(argc, argv, "sn:t:");
} if(opt < 0) {
break;
}
switch(opt) {
case 's':
serial = 1;
break;
case 'n':
n = atoi(optarg);
break;
case 't':
nthreads = atoi(optarg);
break;
default:
goto usage;
}
}
free(a); if(n <= 0) {
return 0; goto usage;
}
if(nthreads < 0 && !serial) {
goto usage;
}
a = malloc(n * sizeof(int));
unsigned long long s = 0;
for(int i = 0; i < n; i++) {
s = s * 6364136223846793005ULL + 1442695040888963407;
a[i] = (int)((s >> 33) & 0x7FFFFFFF);
}
clock_gettime(CLOCK_MONOTONIC, &begin);
if(serial) {
quicksort_serial(a, 0, n - 1);
} else {
rc = sched_init(nthreads, (n + 127) / 128, quicksort,
new_args(a, 0, n - 1));
assert(rc >= 0);
}
clock_gettime(CLOCK_MONOTONIC, &end);
delay = end.tv_sec + end.tv_nsec / 1000000000.0 -
(begin.tv_sec + begin.tv_nsec / 1000000000.0);
printf("Done in %lf seconds.\n", delay);
for(int i = 0; i < n - 1; i++) {
assert(a[i] <= a[i + 1]);
}
free(a);
return 0;
usage: usage:
printf("quicksort [-n size] [-t threads] [-s]\n"); printf("quicksort [-n size] [-t threads] [-s]\n");
return 1; return 1;
} }

View file

@ -6,110 +6,116 @@
static struct scheduler sched; static struct scheduler sched;
/* Lance une tâche de la pile */ /* Lance une tâche de la pile */
void *worker_routine(void *arg) { void *
struct scheduler *s = (struct scheduler *)arg; worker_routine(void *arg)
{
struct scheduler *s = (struct scheduler *)arg;
while (1) { while(1) {
pthread_mutex_lock(&s->mutex);
if(s->exit == 1) {
pthread_mutex_unlock(&s->mutex);
printf("finalement..rien a faire, ciao\n");
break;
}
// S'il on a rien à faire
if(s->top == -1) {
// printf("attend..\n");
pthread_cond_wait(&s->cond, &s->mutex);
pthread_mutex_unlock(&s->mutex);
// printf("reveillé!\n");
continue;
}
// printf("lancement tâche #%d\n", s->top);
// 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);
// Signale s'il n'y a plus rien à faire
if(s->top == -1) {
printf("va falloir partir\n");
pthread_mutex_lock(&s->mutex);
s->exit = 1;
pthread_cond_broadcast(&s->cond);
pthread_mutex_unlock(&s->mutex);
}
}
return NULL;
}
int
sched_init(int nthreads, int qlen, taskfunc f, void *closure)
{
if(nthreads == 0) {
nthreads = sched_default_threads();
}
// TODO : Actuellement on n'utilises pas qlen
// => On utilise une pile de taille fixe
(void)qlen;
sched.top = -1;
sched.exit = 0;
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;
}
pthread_t threads[nthreads];
for(int i = 0; i < nthreads; ++i) {
if(pthread_create(&threads[i], NULL, worker_routine, &sched) != 0) {
fprintf(stderr, "Can't create threads\n");
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;
}
}
return 1;
}
int
sched_spawn(taskfunc f, void *closure, struct scheduler *s)
{
pthread_mutex_lock(&s->mutex); pthread_mutex_lock(&s->mutex);
if (s->exit == 1) { if(s->top + 1 >= MAX_TASKS) {
pthread_mutex_unlock(&s->mutex); pthread_mutex_unlock(&s->mutex);
printf("finalement..rien a faire, ciao\n"); errno = EAGAIN;
fprintf(stderr, "Stack is full\n");
break; return -1;
} }
// S'il on a rien à faire s->tasks[++s->top] = (taskinfo){f, closure};
if (s->top == -1) {
// printf("attend..\n");
pthread_cond_wait(&s->cond, &s->mutex);
pthread_mutex_unlock(&s->mutex);
// printf("reveillé!\n");
continue;
}
// printf("lancement tâche #%d\n", s->top);
// Extrait la tâche de la pile pthread_cond_signal(&s->cond);
taskfunc f = s->tasks[s->top].f;
void *closure = s->tasks[s->top].closure;
s->top--;
pthread_mutex_unlock(&s->mutex); pthread_mutex_unlock(&s->mutex);
// Exécute la tâche return 0;
f(closure, s);
// Signale s'il n'y a plus rien à faire
if (s->top == -1) {
printf("va falloir partir\n");
pthread_mutex_lock(&s->mutex);
s->exit = 1;
pthread_cond_broadcast(&s->cond);
pthread_mutex_unlock(&s->mutex);
}
}
return NULL;
}
int sched_init(int nthreads, int qlen, taskfunc f, void *closure) {
if (nthreads == 0) {
nthreads = sched_default_threads();
}
// TODO : Actuellement on n'utilises pas qlen
// => On utilise une pile de taille fixe
(void)qlen;
sched.top = -1;
sched.exit = 0;
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;
}
pthread_t threads[nthreads];
for (int i = 0; i < nthreads; ++i) {
if (pthread_create(&threads[i], NULL, worker_routine, &sched) != 0) {
fprintf(stderr, "Can't create threads\n");
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;
}
}
return 1;
}
int sched_spawn(taskfunc f, void *closure, struct scheduler *s) {
pthread_mutex_lock(&s->mutex);
if (s->top + 1 >= MAX_TASKS) {
pthread_mutex_unlock(&s->mutex);
errno = EAGAIN;
fprintf(stderr, "Stack is full\n");
return -1;
}
s->tasks[++s->top] = (taskinfo){f, closure};
pthread_cond_signal(&s->cond);
pthread_mutex_unlock(&s->mutex);
return 0;
} }