git.haldean.org ubik / b785760
improve single-threaded performance Haldean Brown 4 years ago
6 changed file(s) with 170 addition(s) and 56 deletion(s). Raw diff Collapse all Expand all
1818
1919 #pragma once
2020 #include "ubik/alloc.h"
21 #include <pthread.h>
21 #include "ubik/mt.h"
2222
2323 struct ubik_deque_elem
2424 {
3232 struct ubik_deque_elem *left;
3333 struct ubik_deque_elem *right;
3434 struct ubik_alloc_region *r;
35 pthread_mutex_t lock;
35 ubik_mutex lock;
3636 };
3737
3838 void
0 /*
1 * mt.h: multithreading support for ubik runtime
2 * Copyright (C) 2018, Haldean Brown
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #pragma once
20
21 #include <pthread.h>
22 #include <stdint.h>
23
24 #define UBIK_DISABLE_MT
25
26 /* Returns the number of threads the Ubik environment should use. Guaranteed to
27 * not change over the lifetime of the environment. */
28 size_t
29 ubik_thread_count();
30
31 typedef pthread_mutex_t ubik_mutex;
32
33 #ifdef UBIK_DISABLE_MT
34 #define ubik_lock(x)
35 #define ubik_unlock(x)
36 #else
37 #define ubik_lock pthread_mutex_lock
38 #define ubik_unlock pthread_mutex_unlock
39 #endif
2626 ./rt/fileport.c \
2727 ./rt/jobq.c \
2828 ./rt/mem.c \
29 ./rt/mt.c \
2930 ./rt/ports.c \
3031 ./rt/rat.c \
3132 ./rt/rwlock.c \
1616 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1717 */
1818
19 #include <pthread.h>
2019 #include <stdatomic.h>
2120 #include <stdlib.h>
2221 #include <string.h>
2625 #include "ubik/assert.h"
2726 #include "ubik/evaluator.h"
2827 #include "ubik/jobq.h"
28 #include "ubik/mt.h"
2929 #include "ubik/rttypes.h"
3030 #include "ubik/string.h"
3131 #include "ubik/ubik.h"
7777 struct ubik_eval_req *req;
7878 size_t n;
7979 size_t term;
80 pthread_mutex_t lock;
80 ubik_mutex lock;
8181 };
8282
8383 struct eval_thread_state
330330 ubik_error err;
331331 bool progress;
332332
333 pthread_mutex_lock(&e->lock);
333 ubik_lock(&e->lock);
334334 err = OK;
335335
336336 #ifdef EVALUATOR_DEBUG
349349 if (err != OK)
350350 goto exit;
351351 e->term = 0;
352 pthread_mutex_unlock(&e->lock);
352 ubik_unlock(&e->lock);
353353 return OK;
354354 }
355355
503503 }
504504 }
505505
506 pthread_mutex_unlock(&e->lock);
506 ubik_unlock(&e->lock);
507507
508508 exit:
509509 if (e->f->gc.traced && (err != OK || e->term == 0))
665665 #endif
666666 if (likely(r->waiting != NULL))
667667 {
668 pthread_mutex_lock(&r->waiting->lock);
668 ubik_lock(&r->waiting->lock);
669669 t0 = r->node;
670670 r->waiting->nv[t0] = r->e->nv[t1];
671671 r->waiting->nt[t0] = r->e->nt[t1];
672672 setstatus(r->waiting, t0, LOADED);
673 pthread_mutex_unlock(&r->waiting->lock);
673 ubik_unlock(&r->waiting->lock);
674674 }
675675 if (unlikely(r->cb != NULL))
676676 {
677 pthread_mutex_lock(&r->e->lock);
677 ubik_lock(&r->e->lock);
678678 err = r->cb->func(
679679 r->cb, r->e->nv[t1], r->e->nt[t1], r->e->nv);
680 pthread_mutex_unlock(&r->e->lock);
680 ubik_unlock(&r->e->lock);
681681 if (err != OK)
682682 {
683683 free_eval_state(r->e);
698698 return err;
699699 }
700700
701 static size_t
702 n_processors()
703 {
704 long res;
705 char *env;
706 char *end;
707
708 env = getenv("UBIK_PROCS");
709 if (env != NULL)
710 {
711 res = strtol(env, &end, 10);
712 if (end != env)
713 return res;
714 }
715 #if defined(_SC_NPROCESSORS_ONLN)
716 res = sysconf(_SC_NPROCESSORS_ONLN);
717 if (res <= 0)
718 {
719 return 1;
720 }
721 return (size_t) res;
722 #else
723 return 1;
724 #endif
725 }
726
727701 no_ignore ubik_error
728702 ubik_evaluate_run(struct ubik_evaluator *evaluator)
729703 {
786760 (*evaluator)->env = env;
787761 (*evaluator)->ws = ws;
788762 (*evaluator)->die = false;
789 (*evaluator)->n_workers = n_processors();
763 (*evaluator)->n_workers = ubik_thread_count();
790764 ubik_jobq_init(&(*evaluator)->q, (*evaluator)->n_workers);
791765
792766 ubik_galloc(
0 /*
1 * mt.c: multithreading support for ubik runtime
2 * Copyright (C) 2018, Haldean Brown
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include "ubik/mt.h"
22
23 #ifndef UBIK_DISABLE_MT
24 static size_t thread_count = 0;
25 #endif
26
27 size_t
28 ubik_thread_count()
29 {
30 #ifdef UBIK_DISABLE_MT
31 return 1;
32 #else
33 long res;
34 char *env;
35 char *end;
36
37 if (thread_count != 0)
38 return thread_count;
39
40 env = getenv("UBIK_PROCS");
41 if (env != NULL)
42 {
43 res = strtol(env, &end, 10);
44 if (end != env)
45 thread_count = res;
46 }
47 else
48 {
49 #if defined(_SC_NPROCESSORS_ONLN)
50 res = sysconf(_SC_NPROCESSORS_ONLN);
51 if (res <= 0)
52 thread_count = 1;
53 thread_count = (size_t) res;
54 #else
55 thread_count 1;
56 #endif
57 }
58 return thread_count;
59 #endif
60 }
2121
2222 #include <stdatomic.h>
2323
24 #define MAX_RECYCLE_SIZE 512
25
26 static struct ubik_deque_elem *recycle = NULL;
27 size_t recycle_size = 0;
28 ubik_mutex recycle_lock;
29
2430 void
2531 ubik_deque_init(struct ubik_deque *d)
2632 {
2935 d->right = NULL;
3036 }
3137
38 inline static struct ubik_deque_elem *
39 acquire_element()
40 {
41 struct ubik_deque_elem *e;
42 ubik_lock(&recycle_lock);
43 if (recycle_size != 0)
44 {
45 e = recycle;
46 recycle = recycle->right;
47 recycle_size--;
48 ubik_unlock(&recycle_lock);
49 return e;
50 }
51 ubik_unlock(&recycle_lock);
52 ubik_galloc((void**) &e, 1, sizeof(struct ubik_deque_elem));
53 return e;
54 }
55
56 static void
57 discard_element(struct ubik_deque_elem *e)
58 {
59 ubik_lock(&recycle_lock);
60 if (recycle_size == MAX_RECYCLE_SIZE)
61 {
62 ubik_unlock(&recycle_lock);
63 free(e);
64 return;
65 }
66 e->right = recycle;
67 recycle = e;
68 recycle_size++;
69 ubik_unlock(&recycle_lock);
70 }
71
3272 void
3373 ubik_deque_pushl(struct ubik_deque *d, void *e)
3474 {
3575 struct ubik_deque_elem *elem;
3676
37 ubik_galloc((void**) &elem, 1, sizeof(struct ubik_deque_elem));
38
39 pthread_mutex_lock(&d->lock);
77 elem = acquire_element();
78 ubik_lock(&d->lock);
4079 elem->e = e;
4180 elem->left = NULL;
4281 elem->right = d->left;
4584 d->left = elem;
4685 if (d->right == NULL)
4786 d->right = elem;
48 pthread_mutex_unlock(&d->lock);
87 ubik_unlock(&d->lock);
4988 }
5089
5190 void
5392 {
5493 struct ubik_deque_elem *elem;
5594
56 ubik_galloc((void**) &elem, 1, sizeof(struct ubik_deque_elem));
57
58 pthread_mutex_lock(&d->lock);
95 elem = acquire_element();
96 ubik_lock(&d->lock);
5997 elem->e = e;
6098 elem->left = d->right;
6199 elem->right = NULL;
64102 d->right = elem;
65103 if (d->left == NULL)
66104 d->left = elem;
67 pthread_mutex_unlock(&d->lock);
105 ubik_unlock(&d->lock);
68106 }
69107
70108 void *
73111 struct ubik_deque_elem *e;
74112 void *v;
75113
76 pthread_mutex_lock(&d->lock);
114 ubik_lock(&d->lock);
77115 if (d->left == NULL)
78116 {
79 pthread_mutex_unlock(&d->lock);
117 ubik_unlock(&d->lock);
80118 return NULL;
81119 }
82120 e = d->left;
86124 else
87125 /* The case where e was the only element in the deque */
88126 d->right = NULL;
89 pthread_mutex_unlock(&d->lock);
127 ubik_unlock(&d->lock);
90128
91129 v = e->e;
92 free(e);
130 discard_element(e);
93131 return v;
94132 }
95133
99137 struct ubik_deque_elem *e;
100138 void *v;
101139
102 pthread_mutex_lock(&d->lock);
140 ubik_lock(&d->lock);
103141 if (d->left == NULL)
104142 {
105 pthread_mutex_unlock(&d->lock);
143 ubik_unlock(&d->lock);
106144 return NULL;
107145 }
108146 e = d->right;
112150 else
113151 /* The case where e was the only element in the deque */
114152 d->left = NULL;
115 pthread_mutex_unlock(&d->lock);
153 ubik_unlock(&d->lock);
116154
117155 v = e->e;
118 free(e);
156 discard_element(e);
119157 return v;
120158 }
121159
124162 {
125163 struct ubik_deque_elem *l;
126164
127 pthread_mutex_lock(&d->lock);
165 ubik_lock(&d->lock);
128166 l = d->left;
129 pthread_mutex_unlock(&d->lock);
167 ubik_unlock(&d->lock);
130168
131169 return l == NULL;
132170 }