improve single-threaded performance
Haldean Brown
4 years ago
18 | 18 | |
19 | 19 | #pragma once |
20 | 20 | #include "ubik/alloc.h" |
21 | #include <pthread.h> | |
21 | #include "ubik/mt.h" | |
22 | 22 | |
23 | 23 | struct ubik_deque_elem |
24 | 24 | { |
32 | 32 | struct ubik_deque_elem *left; |
33 | 33 | struct ubik_deque_elem *right; |
34 | 34 | struct ubik_alloc_region *r; |
35 | pthread_mutex_t lock; | |
35 | ubik_mutex lock; | |
36 | 36 | }; |
37 | 37 | |
38 | 38 | 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 |
26 | 26 | ./rt/fileport.c \ |
27 | 27 | ./rt/jobq.c \ |
28 | 28 | ./rt/mem.c \ |
29 | ./rt/mt.c \ | |
29 | 30 | ./rt/ports.c \ |
30 | 31 | ./rt/rat.c \ |
31 | 32 | ./rt/rwlock.c \ |
16 | 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
17 | 17 | */ |
18 | 18 | |
19 | #include <pthread.h> | |
20 | 19 | #include <stdatomic.h> |
21 | 20 | #include <stdlib.h> |
22 | 21 | #include <string.h> |
26 | 25 | #include "ubik/assert.h" |
27 | 26 | #include "ubik/evaluator.h" |
28 | 27 | #include "ubik/jobq.h" |
28 | #include "ubik/mt.h" | |
29 | 29 | #include "ubik/rttypes.h" |
30 | 30 | #include "ubik/string.h" |
31 | 31 | #include "ubik/ubik.h" |
77 | 77 | struct ubik_eval_req *req; |
78 | 78 | size_t n; |
79 | 79 | size_t term; |
80 | pthread_mutex_t lock; | |
80 | ubik_mutex lock; | |
81 | 81 | }; |
82 | 82 | |
83 | 83 | struct eval_thread_state |
330 | 330 | ubik_error err; |
331 | 331 | bool progress; |
332 | 332 | |
333 | pthread_mutex_lock(&e->lock); | |
333 | ubik_lock(&e->lock); | |
334 | 334 | err = OK; |
335 | 335 | |
336 | 336 | #ifdef EVALUATOR_DEBUG |
349 | 349 | if (err != OK) |
350 | 350 | goto exit; |
351 | 351 | e->term = 0; |
352 | pthread_mutex_unlock(&e->lock); | |
352 | ubik_unlock(&e->lock); | |
353 | 353 | return OK; |
354 | 354 | } |
355 | 355 | |
503 | 503 | } |
504 | 504 | } |
505 | 505 | |
506 | pthread_mutex_unlock(&e->lock); | |
506 | ubik_unlock(&e->lock); | |
507 | 507 | |
508 | 508 | exit: |
509 | 509 | if (e->f->gc.traced && (err != OK || e->term == 0)) |
665 | 665 | #endif |
666 | 666 | if (likely(r->waiting != NULL)) |
667 | 667 | { |
668 | pthread_mutex_lock(&r->waiting->lock); | |
668 | ubik_lock(&r->waiting->lock); | |
669 | 669 | t0 = r->node; |
670 | 670 | r->waiting->nv[t0] = r->e->nv[t1]; |
671 | 671 | r->waiting->nt[t0] = r->e->nt[t1]; |
672 | 672 | setstatus(r->waiting, t0, LOADED); |
673 | pthread_mutex_unlock(&r->waiting->lock); | |
673 | ubik_unlock(&r->waiting->lock); | |
674 | 674 | } |
675 | 675 | if (unlikely(r->cb != NULL)) |
676 | 676 | { |
677 | pthread_mutex_lock(&r->e->lock); | |
677 | ubik_lock(&r->e->lock); | |
678 | 678 | err = r->cb->func( |
679 | 679 | 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); | |
681 | 681 | if (err != OK) |
682 | 682 | { |
683 | 683 | free_eval_state(r->e); |
698 | 698 | return err; |
699 | 699 | } |
700 | 700 | |
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 | ||
727 | 701 | no_ignore ubik_error |
728 | 702 | ubik_evaluate_run(struct ubik_evaluator *evaluator) |
729 | 703 | { |
786 | 760 | (*evaluator)->env = env; |
787 | 761 | (*evaluator)->ws = ws; |
788 | 762 | (*evaluator)->die = false; |
789 | (*evaluator)->n_workers = n_processors(); | |
763 | (*evaluator)->n_workers = ubik_thread_count(); | |
790 | 764 | ubik_jobq_init(&(*evaluator)->q, (*evaluator)->n_workers); |
791 | 765 | |
792 | 766 | 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 | } |
21 | 21 | |
22 | 22 | #include <stdatomic.h> |
23 | 23 | |
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 | ||
24 | 30 | void |
25 | 31 | ubik_deque_init(struct ubik_deque *d) |
26 | 32 | { |
29 | 35 | d->right = NULL; |
30 | 36 | } |
31 | 37 | |
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 | ||
32 | 72 | void |
33 | 73 | ubik_deque_pushl(struct ubik_deque *d, void *e) |
34 | 74 | { |
35 | 75 | struct ubik_deque_elem *elem; |
36 | 76 | |
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); | |
40 | 79 | elem->e = e; |
41 | 80 | elem->left = NULL; |
42 | 81 | elem->right = d->left; |
45 | 84 | d->left = elem; |
46 | 85 | if (d->right == NULL) |
47 | 86 | d->right = elem; |
48 | pthread_mutex_unlock(&d->lock); | |
87 | ubik_unlock(&d->lock); | |
49 | 88 | } |
50 | 89 | |
51 | 90 | void |
53 | 92 | { |
54 | 93 | struct ubik_deque_elem *elem; |
55 | 94 | |
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); | |
59 | 97 | elem->e = e; |
60 | 98 | elem->left = d->right; |
61 | 99 | elem->right = NULL; |
64 | 102 | d->right = elem; |
65 | 103 | if (d->left == NULL) |
66 | 104 | d->left = elem; |
67 | pthread_mutex_unlock(&d->lock); | |
105 | ubik_unlock(&d->lock); | |
68 | 106 | } |
69 | 107 | |
70 | 108 | void * |
73 | 111 | struct ubik_deque_elem *e; |
74 | 112 | void *v; |
75 | 113 | |
76 | pthread_mutex_lock(&d->lock); | |
114 | ubik_lock(&d->lock); | |
77 | 115 | if (d->left == NULL) |
78 | 116 | { |
79 | pthread_mutex_unlock(&d->lock); | |
117 | ubik_unlock(&d->lock); | |
80 | 118 | return NULL; |
81 | 119 | } |
82 | 120 | e = d->left; |
86 | 124 | else |
87 | 125 | /* The case where e was the only element in the deque */ |
88 | 126 | d->right = NULL; |
89 | pthread_mutex_unlock(&d->lock); | |
127 | ubik_unlock(&d->lock); | |
90 | 128 | |
91 | 129 | v = e->e; |
92 | free(e); | |
130 | discard_element(e); | |
93 | 131 | return v; |
94 | 132 | } |
95 | 133 | |
99 | 137 | struct ubik_deque_elem *e; |
100 | 138 | void *v; |
101 | 139 | |
102 | pthread_mutex_lock(&d->lock); | |
140 | ubik_lock(&d->lock); | |
103 | 141 | if (d->left == NULL) |
104 | 142 | { |
105 | pthread_mutex_unlock(&d->lock); | |
143 | ubik_unlock(&d->lock); | |
106 | 144 | return NULL; |
107 | 145 | } |
108 | 146 | e = d->right; |
112 | 150 | else |
113 | 151 | /* The case where e was the only element in the deque */ |
114 | 152 | d->left = NULL; |
115 | pthread_mutex_unlock(&d->lock); | |
153 | ubik_unlock(&d->lock); | |
116 | 154 | |
117 | 155 | v = e->e; |
118 | free(e); | |
156 | discard_element(e); | |
119 | 157 | return v; |
120 | 158 | } |
121 | 159 | |
124 | 162 | { |
125 | 163 | struct ubik_deque_elem *l; |
126 | 164 | |
127 | pthread_mutex_lock(&d->lock); | |
165 | ubik_lock(&d->lock); | |
128 | 166 | l = d->left; |
129 | pthread_mutex_unlock(&d->lock); | |
167 | ubik_unlock(&d->lock); | |
130 | 168 | |
131 | 169 | return l == NULL; |
132 | 170 | } |