diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..deca1f0 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +IndentWidth: 4 +InsertBraces: true +BreakBeforeBraces: Linux +AlwaysBreakAfterReturnType: TopLevelDefinitions +SpaceBeforeParens: Never diff --git a/includes/sched.h b/includes/sched.h index 8a0f77c..1d9839d 100644 --- a/includes/sched.h +++ b/includes/sched.h @@ -10,29 +10,31 @@ struct scheduler; typedef void (*taskfunc)(void *, struct scheduler *); typedef struct task_info { - taskfunc f; - void *closure; + taskfunc f; + void *closure; } taskinfo; struct scheduler { - /* Mutex qui protège la structure */ - pthread_mutex_t mutex; + /* Mutex qui protège la structure */ + pthread_mutex_t mutex; - /* Indicateur de changement d'état */ - pthread_cond_t cond; + /* Indicateur de changement d'état */ + pthread_cond_t cond; - /* Position actuelle dans la pile */ - int top; + /* Position actuelle dans la pile */ + int top; - /* Tâches */ - taskinfo tasks[MAX_TASKS]; + /* Tâches */ + taskinfo tasks[MAX_TASKS]; - /* Si tout est terminé */ - int exit; + /* Si tout est terminé */ + int exit; }; -static inline int sched_default_threads(void) { - return sysconf(_SC_NPROCESSORS_ONLN); +static inline int +sched_default_threads(void) +{ + return sysconf(_SC_NPROCESSORS_ONLN); } /* Lance l'ordonnanceur diff --git a/src/main.c b/src/main.c index 40baf29..2cbb073 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,7 @@ #include "../includes/quicksort.h" -int main(int argc, char *argv[]) { - return benchmark(argc, argv); +int +main(int argc, char *argv[]) +{ + return benchmark(argc, argv); } diff --git a/src/quicksort.c b/src/quicksort.c index 8bb2883..f59b6b7 100644 --- a/src/quicksort.c +++ b/src/quicksort.c @@ -1,151 +1,161 @@ #include "../includes/quicksort.h" -int partition(int *a, int lo, int hi) { - int pivot = a[lo]; - int i = lo - 1; - int j = hi + 1; - int t; - while (1) { - do { - i++; - } while (a[i] < pivot); +int +partition(int *a, int lo, int hi) +{ + int pivot = a[lo]; + int i = lo - 1; + int j = hi + 1; + int t; + while(1) { + do { + i++; + } while(a[i] < pivot); - do { - j--; - } while (a[j] > pivot); + do { + j--; + } while(a[j] > pivot); - if (i >= j) { - return j; + if(i >= 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 { - int *a; - int lo, hi; + int *a; + int lo, hi; }; -struct quicksort_args *new_args(int *a, int lo, int hi) { - 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; +struct quicksort_args * +new_args(int *a, int lo, int hi) +{ + struct quicksort_args *args = malloc(sizeof(struct quicksort_args)); + if(args == NULL) { + return NULL; } - switch (opt) { - case 's': - serial = 1; - break; - case 'n': - n = atoi(optarg); - break; - case 't': - nthreads = atoi(optarg); - break; - default: - goto usage; + + 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; } - } - if (n <= 0) { - goto usage; - } - if (nthreads < 0 && !serial) { - goto usage; - } + p = partition(a, lo, hi); + quicksort_serial(a, lo, p); + quicksort_serial(a, p + 1, hi); +} - 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; - for (int i = 0; i < n; i++) { - s = s * 6364136223846793005ULL + 1442695040888963407; - a[i] = (int)((s >> 33) & 0x7FFFFFFF); - } + free(closure); - clock_gettime(CLOCK_MONOTONIC, &begin); + if(lo >= hi) { + return; + } - if (serial) { - quicksort_serial(a, 0, n - 1); - } else { - rc = - sched_init(nthreads, (n + 127) / 128, quicksort, new_args(a, 0, n - 1)); + 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); +} - 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); +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; - for (int i = 0; i < n - 1; i++) { - assert(a[i] <= a[i + 1]); - } + while(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); - return 0; + if(n <= 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: - printf("quicksort [-n size] [-t threads] [-s]\n"); - return 1; + printf("quicksort [-n size] [-t threads] [-s]\n"); + return 1; } diff --git a/src/sched.c b/src/sched.c index 8dc5152..363a44e 100644 --- a/src/sched.c +++ b/src/sched.c @@ -6,110 +6,116 @@ static struct scheduler sched; /* Lance une tâche de la pile */ -void *worker_routine(void *arg) { - struct scheduler *s = (struct scheduler *)arg; +void * +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); - if (s->exit == 1) { - pthread_mutex_unlock(&s->mutex); - printf("finalement..rien a faire, ciao\n"); - - break; + if(s->top + 1 >= MAX_TASKS) { + pthread_mutex_unlock(&s->mutex); + errno = EAGAIN; + fprintf(stderr, "Stack is full\n"); + return -1; } - // 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); + s->tasks[++s->top] = (taskinfo){f, closure}; - // Extrait la tâche de la pile - taskfunc f = s->tasks[s->top].f; - void *closure = s->tasks[s->top].closure; - s->top--; + pthread_cond_signal(&s->cond); 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); - - 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; + return 0; }