annotate libvtv/vtv_set.h @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
1 /* Copyright (C) 2012-2020 Free Software Foundation, Inc.
111
kono
parents:
diff changeset
2
kono
parents:
diff changeset
3 This file is part of GCC.
kono
parents:
diff changeset
4
kono
parents:
diff changeset
5 GCC is free software; you can redistribute it and/or modify it
kono
parents:
diff changeset
6 under the terms of the GNU General Public License as published by
kono
parents:
diff changeset
7 the Free Software Foundation; either version 3, or (at your option)
kono
parents:
diff changeset
8 any later version.
kono
parents:
diff changeset
9
kono
parents:
diff changeset
10 GCC is distributed in the hope that it will be useful, but WITHOUT
kono
parents:
diff changeset
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
kono
parents:
diff changeset
12 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
kono
parents:
diff changeset
13 License for more details.
kono
parents:
diff changeset
14
kono
parents:
diff changeset
15 Under Section 7 of GPL version 3, you are granted additional
kono
parents:
diff changeset
16 permissions described in the GCC Runtime Library Exception, version
kono
parents:
diff changeset
17 3.1, as published by the Free Software Foundation.
kono
parents:
diff changeset
18
kono
parents:
diff changeset
19 You should have received a copy of the GNU General Public License
kono
parents:
diff changeset
20 and a copy of the GCC Runtime Library Exception along with this
kono
parents:
diff changeset
21 program; see the files COPYING3 and COPYING.RUNTIME respectively.
kono
parents:
diff changeset
22 If not, see <http://www.gnu.org/licenses/>. */
kono
parents:
diff changeset
23
kono
parents:
diff changeset
24 #ifndef _VTV_SET_H
kono
parents:
diff changeset
25 #define _VTV_SET_H 1
kono
parents:
diff changeset
26
kono
parents:
diff changeset
27 /* Code in this file manages a collection of insert-only sets. We
kono
parents:
diff changeset
28 have only tested the case where Key is uintptr_t, though it
kono
parents:
diff changeset
29 theoretically should work for some other cases. All odd keys are
kono
parents:
diff changeset
30 reserved, and must not be inserted into any of the sets. This code
kono
parents:
diff changeset
31 is intended primarily for sets of pointers, and the code is
kono
parents:
diff changeset
32 optimized for small sets (including size 0 and 1), but regardless
kono
parents:
diff changeset
33 of the set size, insert() and contains() have close to O(1) speed
kono
parents:
diff changeset
34 in practice.
kono
parents:
diff changeset
35
kono
parents:
diff changeset
36 TODO(gpike): fix this comment.
kono
parents:
diff changeset
37
kono
parents:
diff changeset
38 Recommended multithreaded use of a set:
kono
parents:
diff changeset
39
kono
parents:
diff changeset
40 For speed, we want to use a lock-free test for set membership. The
kono
parents:
diff changeset
41 code handles simultaneous reads and inserts, as long as at most one
kono
parents:
diff changeset
42 insertion is in progress at a time. After an insert, other threads
kono
parents:
diff changeset
43 may not immediately "see" the inserted key if they perform a
kono
parents:
diff changeset
44 lock-free read, so we recommend retrying, as explained below.
kono
parents:
diff changeset
45
kono
parents:
diff changeset
46 Also, to make data corruption less likely, we recommend using a
kono
parents:
diff changeset
47 "normal" RW page as well as one or pages that are typically RO
kono
parents:
diff changeset
48 but that can be switched to RW and back as needed. The latter
kono
parents:
diff changeset
49 pages should contain sets. The former should contain a lock, L,
kono
parents:
diff changeset
50 and an int or similar, num_writers. Then, to insert, something
kono
parents:
diff changeset
51 like this would be safe:
kono
parents:
diff changeset
52 o Acquire L.
kono
parents:
diff changeset
53 o Increment num_writers; if that made it 1, change pages to RW.
kono
parents:
diff changeset
54 o Release L.
kono
parents:
diff changeset
55 o while (there are insertions to do in some set, S) {
kono
parents:
diff changeset
56 acquire L;
kono
parents:
diff changeset
57 do some insertions in S;
kono
parents:
diff changeset
58 release L;
kono
parents:
diff changeset
59 }
kono
parents:
diff changeset
60 o Acquire L.
kono
parents:
diff changeset
61 o Decrement num_writers; if that made it 0, change pages to RO.
kono
parents:
diff changeset
62 o Release L.
kono
parents:
diff changeset
63
kono
parents:
diff changeset
64 And to check if the set contains some key, one could use
kono
parents:
diff changeset
65 set.contains(key) ||
kono
parents:
diff changeset
66 ({ Acquire L; bool b = set.contains(key); Release L; b; })
kono
parents:
diff changeset
67
kono
parents:
diff changeset
68 In this scheme, the number of threads with reads in progress isn't
kono
parents:
diff changeset
69 tracked, so old sets can never be deleted. In addition, on some
kono
parents:
diff changeset
70 architectures the intentionally racy reads might cause contains()
kono
parents:
diff changeset
71 to return true when it should have returned false. This should be
kono
parents:
diff changeset
72 no problem on x86, and most other machines, where reading or
kono
parents:
diff changeset
73 writing an aligned uintptr_t is atomic. E.g., on those machines,
kono
parents:
diff changeset
74 if *p is 0 and one thread does *p = x while another reads *p, the
kono
parents:
diff changeset
75 read will see either 0 or x.
kono
parents:
diff changeset
76
kono
parents:
diff changeset
77 To make the above easier, the insert_only_hash_sets class provides
kono
parents:
diff changeset
78 an interface to manipulate any number of hash sets. One shouldn't
kono
parents:
diff changeset
79 create objects of that class, as it has no member data and its
kono
parents:
diff changeset
80 methods are static.
kono
parents:
diff changeset
81
kono
parents:
diff changeset
82 So the recommended model is to have a single lock, a single
kono
parents:
diff changeset
83 num_writers variable, and some number of sets. If lock contention
kono
parents:
diff changeset
84 becomes a problem then the sets can be divided into k groups, each
kono
parents:
diff changeset
85 of which has a lock and a num_writers variable; or each set can be
kono
parents:
diff changeset
86 represented as a set of values that equal 0 mod m, a set of values
kono
parents:
diff changeset
87 that equal 1 mod m, ..., plus a set of values that equal m-1 mod m.
kono
parents:
diff changeset
88
kono
parents:
diff changeset
89 However, we expect most or all uses of this code to call contains()
kono
parents:
diff changeset
90 much more frequently than anything else, so lock contention is
kono
parents:
diff changeset
91 likely to be low. */
kono
parents:
diff changeset
92
kono
parents:
diff changeset
93 #include <algorithm>
kono
parents:
diff changeset
94
kono
parents:
diff changeset
95 #ifndef HASHTABLE_STATS
kono
parents:
diff changeset
96 #define HASHTABLE_STATS 0
kono
parents:
diff changeset
97 #endif
kono
parents:
diff changeset
98
kono
parents:
diff changeset
99 #ifndef HASHTABLE_STATS_ATOMIC
kono
parents:
diff changeset
100 #define HASHTABLE_STATS_ATOMIC 0
kono
parents:
diff changeset
101 #endif
kono
parents:
diff changeset
102
kono
parents:
diff changeset
103 #if HASHTABLE_STATS
kono
parents:
diff changeset
104 #if HASHTABLE_STATS_ATOMIC
kono
parents:
diff changeset
105 /* Stat counters, with atomics. */
kono
parents:
diff changeset
106 #include <bits/atomic_word.h>
kono
parents:
diff changeset
107
kono
parents:
diff changeset
108 typedef _Atomic_word _AtomicStatCounter;
kono
parents:
diff changeset
109
kono
parents:
diff changeset
110 void
kono
parents:
diff changeset
111 inc_by (_AtomicStatCounter &stat, int amount)
kono
parents:
diff changeset
112 {
kono
parents:
diff changeset
113 __atomic_add_fetch (&stat, amount, __ATOMIC_ACQ_REL);
kono
parents:
diff changeset
114 }
kono
parents:
diff changeset
115
kono
parents:
diff changeset
116 #else
kono
parents:
diff changeset
117
kono
parents:
diff changeset
118 /* Stat counters, but without atomics. */
kono
parents:
diff changeset
119 typedef int _AtomicStatCounter;
kono
parents:
diff changeset
120
kono
parents:
diff changeset
121 void
kono
parents:
diff changeset
122 inc_by (_AtomicStatCounter& stat, int amount)
kono
parents:
diff changeset
123 {
kono
parents:
diff changeset
124 stat += amount;
kono
parents:
diff changeset
125 }
kono
parents:
diff changeset
126
kono
parents:
diff changeset
127 #endif
kono
parents:
diff changeset
128
kono
parents:
diff changeset
129
kono
parents:
diff changeset
130 /* Number of calls to contains(), insert(), etc. */
kono
parents:
diff changeset
131 extern _AtomicStatCounter stat_insert;
kono
parents:
diff changeset
132 extern _AtomicStatCounter stat_contains;
kono
parents:
diff changeset
133 extern _AtomicStatCounter stat_resize;
kono
parents:
diff changeset
134 extern _AtomicStatCounter stat_create;
kono
parents:
diff changeset
135
kono
parents:
diff changeset
136 /* Sum of set size over all calls to contains(). */
kono
parents:
diff changeset
137 extern _AtomicStatCounter stat_contains_sizes;
kono
parents:
diff changeset
138
kono
parents:
diff changeset
139 /* contains() calls in a set whose capacity is more than 1. */
kono
parents:
diff changeset
140 extern _AtomicStatCounter stat_contains_in_non_trivial_set;
kono
parents:
diff changeset
141
kono
parents:
diff changeset
142 /* Probes in a set whose capacity is more than 1. Ideally, this will
kono
parents:
diff changeset
143 be pretty close to stat_contains_in_non_trivial_set. That will
kono
parents:
diff changeset
144 happen if our hash function is good and/or important keys were
kono
parents:
diff changeset
145 inserted before unimportant keys. */
kono
parents:
diff changeset
146 extern _AtomicStatCounter stat_probes_in_non_trivial_set;
kono
parents:
diff changeset
147
kono
parents:
diff changeset
148 /* number of calls to contains() with size=0, 1, etc. */
kono
parents:
diff changeset
149 extern _AtomicStatCounter stat_contains_size0;
kono
parents:
diff changeset
150 extern _AtomicStatCounter stat_contains_size1;
kono
parents:
diff changeset
151 extern _AtomicStatCounter stat_contains_size2;
kono
parents:
diff changeset
152 extern _AtomicStatCounter stat_contains_size3;
kono
parents:
diff changeset
153 extern _AtomicStatCounter stat_contains_size4;
kono
parents:
diff changeset
154 extern _AtomicStatCounter stat_contains_size5;
kono
parents:
diff changeset
155 extern _AtomicStatCounter stat_contains_size6;
kono
parents:
diff changeset
156 extern _AtomicStatCounter stat_contains_size7;
kono
parents:
diff changeset
157 extern _AtomicStatCounter stat_contains_size8;
kono
parents:
diff changeset
158 extern _AtomicStatCounter stat_contains_size9;
kono
parents:
diff changeset
159 extern _AtomicStatCounter stat_contains_size10;
kono
parents:
diff changeset
160 extern _AtomicStatCounter stat_contains_size11;
kono
parents:
diff changeset
161 extern _AtomicStatCounter stat_contains_size12;
kono
parents:
diff changeset
162 extern _AtomicStatCounter stat_contains_size13_or_more;
kono
parents:
diff changeset
163 extern _AtomicStatCounter stat_grow_from_size0_to_1;
kono
parents:
diff changeset
164 extern _AtomicStatCounter stat_grow_from_size1_to_2;
kono
parents:
diff changeset
165 extern _AtomicStatCounter stat_double_the_number_of_buckets;
kono
parents:
diff changeset
166 extern _AtomicStatCounter stat_insert_key_that_was_already_present;
kono
parents:
diff changeset
167
kono
parents:
diff changeset
168 /* Hash collisions detected during insert_no_resize(). Only counts
kono
parents:
diff changeset
169 hasher(k) == hasher(k'); hasher(k) % tablesize == hasher(k') %
kono
parents:
diff changeset
170 tablesize is not sufficient. Will count collisions that are
kono
parents:
diff changeset
171 detected during table resizes etc., so the same two keys may add to
kono
parents:
diff changeset
172 this stat multiple times. */
kono
parents:
diff changeset
173 extern _AtomicStatCounter stat_insert_found_hash_collision;
kono
parents:
diff changeset
174
kono
parents:
diff changeset
175 #include <string>
kono
parents:
diff changeset
176
kono
parents:
diff changeset
177 struct insert_only_hash_sets_logger
kono
parents:
diff changeset
178 {
kono
parents:
diff changeset
179 static char *
kono
parents:
diff changeset
180 log (char c, char *buf)
kono
parents:
diff changeset
181 {
kono
parents:
diff changeset
182 *buf++ = c;
kono
parents:
diff changeset
183 return buf;
kono
parents:
diff changeset
184 }
kono
parents:
diff changeset
185
kono
parents:
diff changeset
186 static char *
kono
parents:
diff changeset
187 log (const char *s, char *buf)
kono
parents:
diff changeset
188 { return strcpy (buf, s) + strlen (s); }
kono
parents:
diff changeset
189
kono
parents:
diff changeset
190 static char *
kono
parents:
diff changeset
191 log (_AtomicStatCounter i, char *buf)
kono
parents:
diff changeset
192 {
kono
parents:
diff changeset
193 if (i < 10)
kono
parents:
diff changeset
194 return log ((char) ('0' + i), buf);
kono
parents:
diff changeset
195 else
kono
parents:
diff changeset
196 return log ((char) ('0' + i % 10), log (i / 10, buf));
kono
parents:
diff changeset
197 }
kono
parents:
diff changeset
198
kono
parents:
diff changeset
199 static char *
kono
parents:
diff changeset
200 log (const char *label, _AtomicStatCounter i, char *buf)
kono
parents:
diff changeset
201 {
kono
parents:
diff changeset
202 buf = log (label, buf);
kono
parents:
diff changeset
203 buf = log (": ", buf);
kono
parents:
diff changeset
204 buf = log (i, buf);
kono
parents:
diff changeset
205 return log ('\n', buf);
kono
parents:
diff changeset
206 }
kono
parents:
diff changeset
207 };
kono
parents:
diff changeset
208
kono
parents:
diff changeset
209 // Write stats to the given buffer, which should be at least 4000 bytes.
kono
parents:
diff changeset
210 static inline void
kono
parents:
diff changeset
211 insert_only_hash_tables_stats (char *buf)
kono
parents:
diff changeset
212 {
kono
parents:
diff changeset
213 buf = insert_only_hash_sets_logger::log ("insert", stat_insert, buf);
kono
parents:
diff changeset
214 buf = insert_only_hash_sets_logger::log ("contains", stat_contains, buf);
kono
parents:
diff changeset
215 buf = insert_only_hash_sets_logger::log ("resize", stat_resize, buf);
kono
parents:
diff changeset
216 buf = insert_only_hash_sets_logger::log ("create", stat_create, buf);
kono
parents:
diff changeset
217 buf = insert_only_hash_sets_logger::log ("insert_key_that_was_already_"
kono
parents:
diff changeset
218 "present",
kono
parents:
diff changeset
219 stat_insert_key_that_was_already_present,
kono
parents:
diff changeset
220 buf);
kono
parents:
diff changeset
221 buf = insert_only_hash_sets_logger::log ("contains_sizes",
kono
parents:
diff changeset
222 stat_contains_sizes, buf);
kono
parents:
diff changeset
223 buf = insert_only_hash_sets_logger::log ("contains_in_non_trivial_set",
kono
parents:
diff changeset
224 stat_contains_in_non_trivial_set,
kono
parents:
diff changeset
225 buf);
kono
parents:
diff changeset
226 buf = insert_only_hash_sets_logger::log ("probes_in_non_trivial_set",
kono
parents:
diff changeset
227 stat_probes_in_non_trivial_set,
kono
parents:
diff changeset
228 buf);
kono
parents:
diff changeset
229 buf = insert_only_hash_sets_logger::log ("contains_size0",
kono
parents:
diff changeset
230 stat_contains_size0, buf);
kono
parents:
diff changeset
231 buf = insert_only_hash_sets_logger::log ("contains_size1",
kono
parents:
diff changeset
232 stat_contains_size1, buf);
kono
parents:
diff changeset
233 buf = insert_only_hash_sets_logger::log ("contains_size2",
kono
parents:
diff changeset
234 stat_contains_size2, buf);
kono
parents:
diff changeset
235 buf = insert_only_hash_sets_logger::log ("contains_size3",
kono
parents:
diff changeset
236 stat_contains_size3, buf);
kono
parents:
diff changeset
237 buf = insert_only_hash_sets_logger::log ("contains_size4",
kono
parents:
diff changeset
238 stat_contains_size4, buf);
kono
parents:
diff changeset
239 buf = insert_only_hash_sets_logger::log ("contains_size5",
kono
parents:
diff changeset
240 stat_contains_size5, buf);
kono
parents:
diff changeset
241 buf = insert_only_hash_sets_logger::log ("contains_size6",
kono
parents:
diff changeset
242 stat_contains_size6, buf);
kono
parents:
diff changeset
243 buf = insert_only_hash_sets_logger::log ("contains_size7",
kono
parents:
diff changeset
244 stat_contains_size7, buf);
kono
parents:
diff changeset
245 buf = insert_only_hash_sets_logger::log ("contains_size8",
kono
parents:
diff changeset
246 stat_contains_size8, buf);
kono
parents:
diff changeset
247 buf = insert_only_hash_sets_logger::log ("contains_size9",
kono
parents:
diff changeset
248 stat_contains_size9, buf);
kono
parents:
diff changeset
249 buf = insert_only_hash_sets_logger::log ("contains_size10",
kono
parents:
diff changeset
250 stat_contains_size10, buf);
kono
parents:
diff changeset
251 buf = insert_only_hash_sets_logger::log ("contains_size11",
kono
parents:
diff changeset
252 stat_contains_size11, buf);
kono
parents:
diff changeset
253 buf = insert_only_hash_sets_logger::log ("contains_size12",
kono
parents:
diff changeset
254 stat_contains_size12, buf);
kono
parents:
diff changeset
255 buf = insert_only_hash_sets_logger::log ("contains_size13_or_more",
kono
parents:
diff changeset
256 stat_contains_size13_or_more, buf);
kono
parents:
diff changeset
257 buf = insert_only_hash_sets_logger::log ("grow_from_size0_to_1",
kono
parents:
diff changeset
258 stat_grow_from_size0_to_1, buf);
kono
parents:
diff changeset
259 buf = insert_only_hash_sets_logger::log ("grow_from_size1_to_2",
kono
parents:
diff changeset
260 stat_grow_from_size1_to_2, buf);
kono
parents:
diff changeset
261 buf = insert_only_hash_sets_logger::log ("insert_found_hash_collision",
kono
parents:
diff changeset
262 stat_insert_found_hash_collision,
kono
parents:
diff changeset
263 buf);
kono
parents:
diff changeset
264 buf = insert_only_hash_sets_logger::log ("double_the_number_of_buckets",
kono
parents:
diff changeset
265 stat_double_the_number_of_buckets,
kono
parents:
diff changeset
266 buf);
kono
parents:
diff changeset
267 *buf = '\0';
kono
parents:
diff changeset
268 }
kono
parents:
diff changeset
269
kono
parents:
diff changeset
270 #else
kono
parents:
diff changeset
271
kono
parents:
diff changeset
272 /* No stats. */
kono
parents:
diff changeset
273 #define inc_by(statname, amount) do { } while (false && (amount))
kono
parents:
diff changeset
274
kono
parents:
diff changeset
275 #endif
kono
parents:
diff changeset
276
kono
parents:
diff changeset
277 #define inc(statname) inc_by (statname, 1)
kono
parents:
diff changeset
278
kono
parents:
diff changeset
279 template <typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
280 class insert_only_hash_sets
kono
parents:
diff changeset
281 {
kono
parents:
diff changeset
282 public:
kono
parents:
diff changeset
283 typedef Key key_type;
kono
parents:
diff changeset
284 typedef size_t size_type;
kono
parents:
diff changeset
285 typedef Alloc alloc_type;
kono
parents:
diff changeset
286 enum { illegal_key = 1 };
kono
parents:
diff changeset
287 enum { min_capacity = 4 };
kono
parents:
diff changeset
288 #if HASHTABLE_STATS
kono
parents:
diff changeset
289 enum { stats = true };
kono
parents:
diff changeset
290 #else
kono
parents:
diff changeset
291 enum { stats = false };
kono
parents:
diff changeset
292 #endif
kono
parents:
diff changeset
293
kono
parents:
diff changeset
294 /* Do not directly use insert_only_hash_set. Instead, use the
kono
parents:
diff changeset
295 static methods below to create and manipulate objects of the
kono
parents:
diff changeset
296 following class.
kono
parents:
diff changeset
297
kono
parents:
diff changeset
298 Implementation details: each set is represented by a pointer
kono
parents:
diff changeset
299 plus, perhaps, out-of-line data, which would be an object of type
kono
parents:
diff changeset
300 insert_only_hash_set. For a pointer, s, the interpretation is: s
kono
parents:
diff changeset
301 == NULL means empty set, lsb(s) == 1 means a set with one
kono
parents:
diff changeset
302 element, which is (uintptr_t)s - 1, and otherwise s is a pointer
kono
parents:
diff changeset
303 of type insert_only_hash_set*. So, to increase the size of a set
kono
parents:
diff changeset
304 we have to change s and/or *s. To check if a set contains some
kono
parents:
diff changeset
305 key we have to examine s and possibly *s. */
kono
parents:
diff changeset
306 class insert_only_hash_set
kono
parents:
diff changeset
307 {
kono
parents:
diff changeset
308 public:
kono
parents:
diff changeset
309 /* Insert a key. The key must not be a reserved key. */
kono
parents:
diff changeset
310 static inline insert_only_hash_set *insert (key_type key,
kono
parents:
diff changeset
311 insert_only_hash_set *s);
kono
parents:
diff changeset
312
kono
parents:
diff changeset
313
kono
parents:
diff changeset
314 /* Create an empty set. */
kono
parents:
diff changeset
315 static inline insert_only_hash_set *create (size_type capacity);
kono
parents:
diff changeset
316
kono
parents:
diff changeset
317 /* Return whether the given key is present. If key is illegal_key
kono
parents:
diff changeset
318 then either true or false may be returned, but for all other
kono
parents:
diff changeset
319 reserved keys false will be returned. */
kono
parents:
diff changeset
320 static bool
kono
parents:
diff changeset
321 contains (key_type key, const insert_only_hash_set *s)
kono
parents:
diff changeset
322 {
kono
parents:
diff changeset
323 if (stats)
kono
parents:
diff changeset
324 {
kono
parents:
diff changeset
325 inc (stat_contains);
kono
parents:
diff changeset
326 switch (size (s))
kono
parents:
diff changeset
327 {
kono
parents:
diff changeset
328 case 0: inc (stat_contains_size0); break;
kono
parents:
diff changeset
329 case 1: inc (stat_contains_size1); break;
kono
parents:
diff changeset
330 case 2: inc (stat_contains_size2); break;
kono
parents:
diff changeset
331 case 3: inc (stat_contains_size3); break;
kono
parents:
diff changeset
332 case 4: inc (stat_contains_size4); break;
kono
parents:
diff changeset
333 case 5: inc (stat_contains_size5); break;
kono
parents:
diff changeset
334 case 6: inc (stat_contains_size6); break;
kono
parents:
diff changeset
335 case 7: inc (stat_contains_size7); break;
kono
parents:
diff changeset
336 case 8: inc (stat_contains_size8); break;
kono
parents:
diff changeset
337 case 9: inc (stat_contains_size9); break;
kono
parents:
diff changeset
338 case 10: inc (stat_contains_size10); break;
kono
parents:
diff changeset
339 case 11: inc (stat_contains_size11); break;
kono
parents:
diff changeset
340 case 12: inc (stat_contains_size12); break;
kono
parents:
diff changeset
341 default: inc (stat_contains_size13_or_more); break;
kono
parents:
diff changeset
342 }
kono
parents:
diff changeset
343 inc_by (stat_contains_sizes, size (s));
kono
parents:
diff changeset
344 }
kono
parents:
diff changeset
345
kono
parents:
diff changeset
346 return (singleton (s) ?
kono
parents:
diff changeset
347 singleton_key (key) == s :
kono
parents:
diff changeset
348 ((s != NULL) && s->contains (key)));
kono
parents:
diff changeset
349 }
kono
parents:
diff changeset
350
kono
parents:
diff changeset
351 /* Return a set's size. */
kono
parents:
diff changeset
352 static size_type
kono
parents:
diff changeset
353 size (const insert_only_hash_set *s)
kono
parents:
diff changeset
354 { return (s == NULL) ? 0 : (singleton (s) ? 1 : s->num_entries); }
kono
parents:
diff changeset
355
kono
parents:
diff changeset
356 static inline insert_only_hash_set *resize (size_type target_num_buckets,
kono
parents:
diff changeset
357 insert_only_hash_set *s);
kono
parents:
diff changeset
358
kono
parents:
diff changeset
359
kono
parents:
diff changeset
360 private:
kono
parents:
diff changeset
361 /* Return whether a set has size 1. */
kono
parents:
diff changeset
362 static bool
kono
parents:
diff changeset
363 singleton (const insert_only_hash_set *s)
kono
parents:
diff changeset
364 { return (uintptr_t) s & 1; }
kono
parents:
diff changeset
365
kono
parents:
diff changeset
366 /* Return the representation of a singleton set containing the
kono
parents:
diff changeset
367 given key. */
kono
parents:
diff changeset
368 static insert_only_hash_set *
kono
parents:
diff changeset
369 singleton_key (key_type key)
kono
parents:
diff changeset
370 { return (insert_only_hash_set *) ((uintptr_t) key + 1); }
kono
parents:
diff changeset
371
kono
parents:
diff changeset
372 /* Given a singleton set, what key does it contain? */
kono
parents:
diff changeset
373 static key_type
kono
parents:
diff changeset
374 extract_singleton_key (const insert_only_hash_set *s)
kono
parents:
diff changeset
375 {
kono
parents:
diff changeset
376 VTV_DEBUG_ASSERT (singleton (s));
kono
parents:
diff changeset
377 return (key_type) ((uintptr_t) s - 1);
kono
parents:
diff changeset
378 }
kono
parents:
diff changeset
379
kono
parents:
diff changeset
380 volatile key_type &
kono
parents:
diff changeset
381 key_at_index (size_type index)
kono
parents:
diff changeset
382 { return buckets[index]; }
kono
parents:
diff changeset
383
kono
parents:
diff changeset
384 key_type
kono
parents:
diff changeset
385 key_at_index (size_type index) const
kono
parents:
diff changeset
386 { return buckets[index]; }
kono
parents:
diff changeset
387
kono
parents:
diff changeset
388 size_type
kono
parents:
diff changeset
389 next_index (size_type index, size_type indices_examined) const
kono
parents:
diff changeset
390 { return (index + indices_examined) & (num_buckets - 1); }
kono
parents:
diff changeset
391
kono
parents:
diff changeset
392 inline void insert_no_resize (key_type key);
kono
parents:
diff changeset
393
kono
parents:
diff changeset
394 inline bool contains (key_type key) const;
kono
parents:
diff changeset
395
kono
parents:
diff changeset
396 inline insert_only_hash_set *resize_if_necessary (void);
kono
parents:
diff changeset
397
kono
parents:
diff changeset
398 size_type num_buckets; /* Must be a power of 2 not less than
kono
parents:
diff changeset
399 min_capacity. */
kono
parents:
diff changeset
400 volatile size_type num_entries;
kono
parents:
diff changeset
401 volatile key_type buckets[0]; /* Actual array size is num_buckets. */
kono
parents:
diff changeset
402 };
kono
parents:
diff changeset
403
kono
parents:
diff changeset
404 /* Create an empty set with the given capacity. Requires that n be
kono
parents:
diff changeset
405 0 or a power of 2. If 1 < n < min_capacity then treat n as
kono
parents:
diff changeset
406 min_capacity. Sets *handle. Returns true unless the allocator
kono
parents:
diff changeset
407 fails. Subsequent operations on this set should use the same
kono
parents:
diff changeset
408 handle. */
kono
parents:
diff changeset
409
kono
parents:
diff changeset
410 static inline bool create (size_type n, insert_only_hash_set **handle);
kono
parents:
diff changeset
411
kono
parents:
diff changeset
412 /* Force the capacity of a set to be n, unless it was more than n
kono
parents:
diff changeset
413 already. Requires that n be 0 or a power of 2. Sets *handle
kono
parents:
diff changeset
414 unless the current capacity is n or more. Returns true unless
kono
parents:
diff changeset
415 the allocator fails. */
kono
parents:
diff changeset
416
kono
parents:
diff changeset
417 static inline bool resize (size_type n, insert_only_hash_set **handle);
kono
parents:
diff changeset
418
kono
parents:
diff changeset
419 /* Insert a key. *handle is unmodified unless (1) a resize occurs,
kono
parents:
diff changeset
420 or (2) the set was initially empty. Returns true unless the
kono
parents:
diff changeset
421 allocator fails during a resize. If the allocator fails during a
kono
parents:
diff changeset
422 resize then the set is reset to be the empty set. The key must
kono
parents:
diff changeset
423 not be a reserved key. */
kono
parents:
diff changeset
424
kono
parents:
diff changeset
425 static inline bool insert (key_type key, insert_only_hash_set **handle);
kono
parents:
diff changeset
426
kono
parents:
diff changeset
427 /* Check for the presence of a key. If key is illegal_key then
kono
parents:
diff changeset
428 either true or false may be returned, but for all other reserved
kono
parents:
diff changeset
429 keys false will be returned. */
kono
parents:
diff changeset
430
kono
parents:
diff changeset
431 static inline bool
kono
parents:
diff changeset
432 contains (key_type key, /* const */ insert_only_hash_set **handle)
kono
parents:
diff changeset
433 { return insert_only_hash_set::contains (key, *handle); }
kono
parents:
diff changeset
434
kono
parents:
diff changeset
435 /* Return the size of the given set. */
kono
parents:
diff changeset
436 static size_type
kono
parents:
diff changeset
437 size (const insert_only_hash_set **handle)
kono
parents:
diff changeset
438 { return insert_only_hash_set::size (*handle); }
kono
parents:
diff changeset
439
kono
parents:
diff changeset
440 static bool
kono
parents:
diff changeset
441 is_reserved_key (key_type key)
kono
parents:
diff changeset
442 { return ((uintptr_t) key % 2) == 1; }
kono
parents:
diff changeset
443 };
kono
parents:
diff changeset
444
kono
parents:
diff changeset
445 template <typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
446 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
kono
parents:
diff changeset
447 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::resize
kono
parents:
diff changeset
448 (size_type n, insert_only_hash_set *s)
kono
parents:
diff changeset
449 {
kono
parents:
diff changeset
450 if (s == NULL)
kono
parents:
diff changeset
451 return create (n);
kono
parents:
diff changeset
452
kono
parents:
diff changeset
453 size_type capacity = singleton (s) ? 1 : s->num_buckets;
kono
parents:
diff changeset
454
kono
parents:
diff changeset
455 if (n <= capacity)
kono
parents:
diff changeset
456 return s;
kono
parents:
diff changeset
457
kono
parents:
diff changeset
458 insert_only_hash_set *result =
kono
parents:
diff changeset
459 create (std::max<size_type> (n, min_capacity));
kono
parents:
diff changeset
460 if (result != NULL)
kono
parents:
diff changeset
461 {
kono
parents:
diff changeset
462 if (singleton (s))
kono
parents:
diff changeset
463 {
kono
parents:
diff changeset
464 result->insert_no_resize (extract_singleton_key (s));
kono
parents:
diff changeset
465 }
kono
parents:
diff changeset
466 else
kono
parents:
diff changeset
467 {
kono
parents:
diff changeset
468 for (size_type i = 0; i < s->num_buckets; i++)
kono
parents:
diff changeset
469 if (s->buckets[i] != (key_type) illegal_key)
kono
parents:
diff changeset
470 result->insert_no_resize (s->buckets[i]);
kono
parents:
diff changeset
471 }
kono
parents:
diff changeset
472 VTV_DEBUG_ASSERT (size (result) == size (s));
kono
parents:
diff changeset
473 }
kono
parents:
diff changeset
474 return result;
kono
parents:
diff changeset
475 }
kono
parents:
diff changeset
476
kono
parents:
diff changeset
477 template <typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
478 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
kono
parents:
diff changeset
479 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::insert
kono
parents:
diff changeset
480 (key_type key, insert_only_hash_set *s)
kono
parents:
diff changeset
481 {
kono
parents:
diff changeset
482 VTV_DEBUG_ASSERT (!is_reserved_key (key));
kono
parents:
diff changeset
483
kono
parents:
diff changeset
484 inc_by (stat_grow_from_size0_to_1, s == NULL);
kono
parents:
diff changeset
485
kono
parents:
diff changeset
486 if (s == NULL)
kono
parents:
diff changeset
487 return singleton_key (key);
kono
parents:
diff changeset
488
kono
parents:
diff changeset
489 if (singleton (s))
kono
parents:
diff changeset
490 {
kono
parents:
diff changeset
491 const key_type old_key = extract_singleton_key (s);
kono
parents:
diff changeset
492 if (old_key == key)
kono
parents:
diff changeset
493 return s;
kono
parents:
diff changeset
494
kono
parents:
diff changeset
495 /* Grow from size 1 to size 2. */
kono
parents:
diff changeset
496 inc (stat_grow_from_size1_to_2);
kono
parents:
diff changeset
497 s = create (2);
kono
parents:
diff changeset
498 if (s == NULL)
kono
parents:
diff changeset
499 return NULL;
kono
parents:
diff changeset
500
kono
parents:
diff changeset
501 s->insert_no_resize (old_key);
kono
parents:
diff changeset
502 s->insert_no_resize (key);
kono
parents:
diff changeset
503 VTV_DEBUG_ASSERT (size (s) == 2);
kono
parents:
diff changeset
504 return s;
kono
parents:
diff changeset
505 }
kono
parents:
diff changeset
506 s = s->resize_if_necessary();
kono
parents:
diff changeset
507 if (s != NULL)
kono
parents:
diff changeset
508 s->insert_no_resize (key);
kono
parents:
diff changeset
509 return s;
kono
parents:
diff changeset
510 }
kono
parents:
diff changeset
511
kono
parents:
diff changeset
512 template <typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
513 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
kono
parents:
diff changeset
514 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::create
kono
parents:
diff changeset
515 (size_type capacity)
kono
parents:
diff changeset
516 {
kono
parents:
diff changeset
517 if (capacity <= 1)
kono
parents:
diff changeset
518 return NULL;
kono
parents:
diff changeset
519
kono
parents:
diff changeset
520 VTV_DEBUG_ASSERT (capacity > 1 && (capacity & (capacity - 1)) == 0);
kono
parents:
diff changeset
521 VTV_DEBUG_ASSERT (sizeof (insert_only_hash_set) == 2 * sizeof (size_type));
kono
parents:
diff changeset
522 capacity = std::max <size_type> (capacity, min_capacity);
kono
parents:
diff changeset
523 const size_t num_bytes = sizeof (insert_only_hash_set) +
kono
parents:
diff changeset
524 sizeof (key_type) * capacity;
kono
parents:
diff changeset
525 alloc_type alloc;
kono
parents:
diff changeset
526 insert_only_hash_set *result = (insert_only_hash_set *) alloc (num_bytes);
kono
parents:
diff changeset
527 result->num_buckets = capacity;
kono
parents:
diff changeset
528 result->num_entries = 0;
kono
parents:
diff changeset
529 for (size_type i = 0; i < capacity; i++)
kono
parents:
diff changeset
530 result->buckets[i] = (key_type) illegal_key;
kono
parents:
diff changeset
531 return result;
kono
parents:
diff changeset
532 }
kono
parents:
diff changeset
533
kono
parents:
diff changeset
534 template <typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
535 void
kono
parents:
diff changeset
536 insert_only_hash_sets<Key, HashFcn,
kono
parents:
diff changeset
537 Alloc>::insert_only_hash_set::insert_no_resize
kono
parents:
diff changeset
538 (key_type key)
kono
parents:
diff changeset
539 {
kono
parents:
diff changeset
540 HashFcn hasher;
kono
parents:
diff changeset
541 const size_type capacity = num_buckets;
kono
parents:
diff changeset
542 VTV_DEBUG_ASSERT (capacity >= min_capacity);
kono
parents:
diff changeset
543 VTV_DEBUG_ASSERT (!is_reserved_key (key));
kono
parents:
diff changeset
544 size_type index = hasher (key) & (capacity - 1);
kono
parents:
diff changeset
545 key_type k = key_at_index (index);
kono
parents:
diff changeset
546 size_type indices_examined = 0;
kono
parents:
diff changeset
547 while (k != key)
kono
parents:
diff changeset
548 {
kono
parents:
diff changeset
549 ++indices_examined;
kono
parents:
diff changeset
550 if (k == (key_type) illegal_key)
kono
parents:
diff changeset
551 {
kono
parents:
diff changeset
552 key_at_index (index) = key;
kono
parents:
diff changeset
553 ++num_entries;
kono
parents:
diff changeset
554 return;
kono
parents:
diff changeset
555 }
kono
parents:
diff changeset
556 else
kono
parents:
diff changeset
557 {
kono
parents:
diff changeset
558 inc_by (stat_insert_found_hash_collision,
kono
parents:
diff changeset
559 hasher (k) == hasher (key));
kono
parents:
diff changeset
560 }
kono
parents:
diff changeset
561 VTV_DEBUG_ASSERT (indices_examined < capacity);
kono
parents:
diff changeset
562 index = next_index (index, indices_examined);
kono
parents:
diff changeset
563 k = key_at_index (index);
kono
parents:
diff changeset
564 }
kono
parents:
diff changeset
565 }
kono
parents:
diff changeset
566
kono
parents:
diff changeset
567 template<typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
568 bool
kono
parents:
diff changeset
569 insert_only_hash_sets<Key, HashFcn, Alloc>::insert_only_hash_set::contains
kono
parents:
diff changeset
570 (key_type key) const
kono
parents:
diff changeset
571 {
kono
parents:
diff changeset
572 inc (stat_contains_in_non_trivial_set);
kono
parents:
diff changeset
573 HashFcn hasher;
kono
parents:
diff changeset
574 const size_type capacity = num_buckets;
kono
parents:
diff changeset
575 size_type index = hasher (key) & (capacity - 1);
kono
parents:
diff changeset
576 key_type k = key_at_index (index);
kono
parents:
diff changeset
577 size_type indices_examined = 0;
kono
parents:
diff changeset
578 inc (stat_probes_in_non_trivial_set);
kono
parents:
diff changeset
579 while (k != key)
kono
parents:
diff changeset
580 {
kono
parents:
diff changeset
581 ++indices_examined;
kono
parents:
diff changeset
582 if (/*UNLIKELY*/(k == (key_type) illegal_key
kono
parents:
diff changeset
583 || indices_examined == capacity))
kono
parents:
diff changeset
584 return false;
kono
parents:
diff changeset
585
kono
parents:
diff changeset
586 index = next_index (index, indices_examined);
kono
parents:
diff changeset
587 k = key_at_index (index);
kono
parents:
diff changeset
588 inc (stat_probes_in_non_trivial_set);
kono
parents:
diff changeset
589 }
kono
parents:
diff changeset
590 return true;
kono
parents:
diff changeset
591 }
kono
parents:
diff changeset
592
kono
parents:
diff changeset
593 template <typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
594 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
kono
parents:
diff changeset
595 insert_only_hash_sets<Key, HashFcn,
kono
parents:
diff changeset
596 Alloc>::insert_only_hash_set::resize_if_necessary (void)
kono
parents:
diff changeset
597 {
kono
parents:
diff changeset
598 VTV_DEBUG_ASSERT (num_buckets >= min_capacity);
kono
parents:
diff changeset
599 size_type unused = num_buckets - num_entries;
kono
parents:
diff changeset
600 if (unused < (num_buckets >> 2))
kono
parents:
diff changeset
601 {
kono
parents:
diff changeset
602 inc (stat_double_the_number_of_buckets);
kono
parents:
diff changeset
603 size_type new_num_buckets = num_buckets * 2;
kono
parents:
diff changeset
604 insert_only_hash_set *s = create (new_num_buckets);
kono
parents:
diff changeset
605 for (size_type i = 0; i < num_buckets; i++)
kono
parents:
diff changeset
606 if (buckets[i] != (key_type) illegal_key)
kono
parents:
diff changeset
607 s->insert_no_resize (buckets[i]);
kono
parents:
diff changeset
608 VTV_DEBUG_ASSERT (size (this) == size (s));
kono
parents:
diff changeset
609 return s;
kono
parents:
diff changeset
610 }
kono
parents:
diff changeset
611 else
kono
parents:
diff changeset
612 return this;
kono
parents:
diff changeset
613 }
kono
parents:
diff changeset
614
kono
parents:
diff changeset
615 template<typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
616 bool
kono
parents:
diff changeset
617 insert_only_hash_sets<Key, HashFcn, Alloc>::create (size_type n,
kono
parents:
diff changeset
618 insert_only_hash_set **handle)
kono
parents:
diff changeset
619
kono
parents:
diff changeset
620 {
kono
parents:
diff changeset
621 inc (stat_create);
kono
parents:
diff changeset
622 *handle = insert_only_hash_set::create (n);
kono
parents:
diff changeset
623 return (n <= 1) || (*handle != NULL);
kono
parents:
diff changeset
624 }
kono
parents:
diff changeset
625
kono
parents:
diff changeset
626 template<typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
627 bool
kono
parents:
diff changeset
628 insert_only_hash_sets<Key, HashFcn, Alloc>::resize (size_type n,
kono
parents:
diff changeset
629 insert_only_hash_set **handle)
kono
parents:
diff changeset
630 {
kono
parents:
diff changeset
631 inc (stat_resize);
kono
parents:
diff changeset
632 *handle = insert_only_hash_set::resize (n, *handle);
kono
parents:
diff changeset
633 return (n <= 1) || (*handle != NULL);
kono
parents:
diff changeset
634 }
kono
parents:
diff changeset
635
kono
parents:
diff changeset
636 template<typename Key, class HashFcn, class Alloc>
kono
parents:
diff changeset
637 bool
kono
parents:
diff changeset
638 insert_only_hash_sets<Key, HashFcn, Alloc>::insert (key_type key,
kono
parents:
diff changeset
639 insert_only_hash_set **handle)
kono
parents:
diff changeset
640 {
kono
parents:
diff changeset
641 inc (stat_insert);
kono
parents:
diff changeset
642 const size_type old_size = insert_only_hash_set::size (*handle);
kono
parents:
diff changeset
643 *handle = insert_only_hash_set::insert (key, *handle);
kono
parents:
diff changeset
644 if (*handle != NULL)
kono
parents:
diff changeset
645 {
kono
parents:
diff changeset
646 const size_type delta = insert_only_hash_set::size (*handle) - old_size;
kono
parents:
diff changeset
647 inc_by (stat_insert_key_that_was_already_present, delta == 0);
kono
parents:
diff changeset
648 }
kono
parents:
diff changeset
649 return *handle != NULL;
kono
parents:
diff changeset
650 }
kono
parents:
diff changeset
651
kono
parents:
diff changeset
652 #endif /* VTV_SET_H */