reformat, following initial convention
This commit is contained in:
parent
f95dcaf491
commit
6b891cf338
5 changed files with 266 additions and 241 deletions
5
.clang-format
Normal file
5
.clang-format
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
IndentWidth: 4
|
||||||
|
InsertBraces: true
|
||||||
|
BreakBeforeBraces: Linux
|
||||||
|
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||||
|
SpaceBeforeParens: Never
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
264
src/quicksort.c
264
src/quicksort.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
202
src/sched.c
202
src/sched.c
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue