VirtualBox

source: kBuild/trunk/src/kmk/strcache.c@ 1878

Last change on this file since 1878 was 1870, checked in by bird, 16 years ago

kmk: replaced strcache with strcacahe2.

  • Property svn:eol-style set to native
File size: 13.3 KB
Line 
1/* Constant string caching for GNU Make.
2Copyright (C) 2006 Free Software Foundation, Inc.
3This file is part of GNU Make.
4
5GNU Make is free software; you can redistribute it and/or modify it under the
6terms of the GNU General Public License as published by the Free Software
7Foundation; either version 2, or (at your option) any later version.
8
9GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13You should have received a copy of the GNU General Public License along with
14GNU Make; see the file COPYING. If not, write to the Free Software
15Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
16
17#include "make.h"
18#ifndef CONFIG_WITH_STRCACHE2
19
20#include <assert.h>
21
22#include "hash.h"
23
24
25/* The size (in bytes) of each cache buffer.
26 Try to pick something that will map well into the heap. */
27#define CACHE_BUFFER_SIZE (8192 - 16)
28
29
30/* A string cached here will never be freed, so we don't need to worry about
31 reference counting. We just store the string, and then remember it in a
32 hash so it can be looked up again. */
33
34struct strcache {
35 struct strcache *next; /* The next block of strings. */
36 char *end; /* Pointer to the beginning of the free space. */
37 int count; /* # of strings in this buffer (for stats). */
38 int bytesfree; /* The amount of the buffer that is free. */
39 char buffer[1]; /* The buffer comes after this. */
40};
41
42static int bufsize = CACHE_BUFFER_SIZE;
43static struct strcache *strcache = NULL;
44
45/* Add a new buffer to the cache. Add it at the front to reduce search time.
46 This can also increase the overhead, since it's less likely that older
47 buffers will be filled in. However, GNU make has so many smaller strings
48 that this doesn't seem to be much of an issue in practice.
49 */
50static struct strcache *
51new_cache()
52{
53 struct strcache *new;
54 new = xmalloc (sizeof (*new) + bufsize);
55 new->end = new->buffer;
56 new->count = 0;
57 new->bytesfree = bufsize;
58
59 new->next = strcache;
60 strcache = new;
61
62 return new;
63}
64
65#ifndef CONFIG_WITH_VALUE_LENGTH
66static const char *
67add_string(const char *str, int len)
68{
69 struct strcache *best = NULL;
70 struct strcache *sp;
71 const char *res;
72
73 /* If the string we want is too large to fit into a single buffer, then
74 we're screwed; nothing will ever fit! Change the maximum size of the
75 cache to be big enough. */
76 if (len > bufsize)
77 bufsize = len * 2;
78
79 /* First, find a cache with enough free space. We always look through all
80 the blocks and choose the one with the best fit (the one that leaves the
81 least amount of space free). */
82 for (sp = strcache; sp != NULL; sp = sp->next)
83 if (sp->bytesfree > len && (!best || best->bytesfree > sp->bytesfree))
84 best = sp;
85
86 /* If nothing is big enough, make a new cache. */
87 if (!best)
88 best = new_cache();
89
90 assert (best->bytesfree > len);
91
92 /* Add the string to the best cache. */
93 res = best->end;
94 memcpy (best->end, str, len);
95 best->end += len;
96 *(best->end++) = '\0';
97 best->bytesfree -= len + 1;
98 ++best->count;
99 return res;
100}
101
102#else /* CONFIG_WITH_VALUE_LENGTH */
103
104static const char *
105add_string(const char *str, struct strcache_pref *prefix)
106{
107 struct strcache *best = NULL;
108 struct strcache *sp;
109 struct strcache_pref *real_prefix;
110 int len;
111 const char *res;
112 char *dst;
113
114 /* Calc the entry length; the prefix data + the string + alignment. */
115 len = sizeof(struct strcache_pref) + prefix->len + 1;
116 len = (len + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
117
118 /* If the string we want is too large to fit into a single buffer, then
119 we're screwed; nothing will ever fit! Change the maximum size of the
120 cache to be big enough. */
121 if (len > bufsize)
122 bufsize = len * 2;
123
124 /* First, find a cache with enough free space. We always look through all
125 the blocks and choose the one with the best fit (the one that leaves the
126 least amount of space free). */
127 for (sp = strcache; sp != NULL; sp = sp->next)
128 if (sp->bytesfree >= len && (!best || best->bytesfree > sp->bytesfree))
129 best = sp;
130
131 /* If nothing is big enough, make a new cache. */
132 if (!best)
133 best = new_cache();
134
135 assert (best->bytesfree >= len);
136
137 /* Add the string to the best cache. */
138 real_prefix = (struct strcache_pref *)best->end;
139 *real_prefix = *prefix;
140
141 res = dst = (char *)(real_prefix + 1);
142 assert(!((size_t)res & (sizeof(void *) - 1)));
143 memcpy (dst, str, prefix->len);
144 dst += prefix->len;
145 *(dst++) = '\0';
146
147 best->end += len;
148 best->bytesfree -= len;
149 ++best->count;
150
151 return res;
152}
153
154/* Hackish globals for passing data to the hash functions.
155 There isn't really any other way without running the
156 risk of breaking rehashing. */
157static const char *lookup_string;
158static struct strcache_pref *lookup_prefix;
159
160#endif /* CONFIG_WITH_VALUE_LENGTH */
161
162
163/* Hash table of strings in the cache. */
164
165static unsigned long
166str_hash_1 (const void *key)
167{
168#if 0
169#ifdef CONFIG_WITH_VALUE_LENGTH
170 if (MY_PREDICT_TRUE ((const char *) key == lookup_string))
171 return lookup_prefix->hash1;
172#endif
173#endif
174 return_ISTRING_HASH_1 ((const char *) key);
175}
176
177static unsigned long
178str_hash_2 (const void *key)
179{
180#ifdef CONFIG_WITH_VALUE_LENGTH
181 if (MY_PREDICT_TRUE ((const char *) key == lookup_string))
182 {
183 if (lookup_prefix->hash2)
184 {
185 unsigned long hash2 = 0;
186 ISTRING_HASH_2 ((const char *)key, hash2);
187 lookup_prefix->hash2 = hash2;
188 }
189 return lookup_prefix->hash2;
190 }
191#endif
192 return_ISTRING_HASH_2 ((const char *) key);
193}
194
195static int
196str_hash_cmp (const void *x, const void *y)
197{
198#ifdef CONFIG_WITH_VALUE_LENGTH
199 /* Use the string length to avoid some unncessary comparing.
200 X is either the add_hash input (during hash_find_slot)
201 or a cache entry (during rare hash_insert_at calls).
202 This catches 520253 out of 1341947 calls in the typical
203 kBuild scenario. */
204
205 if (MY_PREDICT_TRUE ((const char *) x == lookup_string))
206 {
207 assert (lookup_prefix->len == strlen ((const char *)x));
208 if (strcache_get_len ((const char *)y) != lookup_prefix->len)
209 return -1;
210 }
211#endif
212 return_ISTRING_COMPARE ((const char *) x, (const char *) y);
213}
214
215static struct hash_table strings;
216static unsigned long total_adds = 0;
217
218#ifndef CONFIG_WITH_VALUE_LENGTH
219static const char *
220add_hash (const char *str, int len)
221{
222 /* Look up the string in the hash. If it's there, return it. */
223 char *const *slot = (char *const *) hash_find_slot (&strings, str);
224 const char *key = *slot;
225
226 /* Count the total number of adds we performed. */
227 ++total_adds;
228
229 if (!HASH_VACANT (key))
230 return key;
231
232 /* Not there yet so add it to a buffer, then into the hash table. */
233 key = add_string (str, len);
234 hash_insert_at (&strings, key, slot);
235 return key;
236}
237
238#else /* CONFIG_WITH_VALUE_LENGTH */
239
240static const char *
241add_hash (const char *str, int len, unsigned long hash1, unsigned long hash2)
242{
243 void const **slot;
244 const char *key;
245 struct strcache_pref prefix;
246
247 /* Look up the string in the hash. If it's there, return it. */
248 prefix.len = len;
249 prefix.hash2 = hash2;
250 if (!hash1 && !hash2)
251 ISTRING_HASH_1 (str, hash1);
252 prefix.hash1 = hash1;
253
254 lookup_string = str;
255 lookup_prefix = &prefix;
256
257 slot = hash_find_slot_prehashed (&strings, str, prefix.hash1, prefix.hash2);
258 key = *(const char **)slot;
259
260 /* Count the total number of adds we performed. */
261 ++total_adds;
262
263 if (!HASH_VACANT (key))
264 return key;
265
266 /* Not there yet so add it to a buffer, then into the hash table. */
267 key = add_string (str, &prefix);
268 hash_insert_at (&strings, key, slot);
269 return key;
270}
271
272/* Verifies that a string cache entry didn't change and that the
273 prefix is still valid. */
274int
275strcache_check_sanity (const char *str)
276{
277 struct strcache_pref const *prefix = (struct strcache_pref const *)str - 1;
278 unsigned long hash;
279
280 if (strlen (str) != prefix->len)
281 {
282 MY_ASSERT_MSG (0, ("len: %u != %u - '%s'\n", (unsigned int)strlen (str),
283 prefix->len, str));
284 return -1;
285 }
286
287 hash = 0;
288 ISTRING_HASH_1 (str, hash);
289 if (hash != prefix->hash1)
290 {
291 MY_ASSERT_MSG (0, ("hash1: %lx != %lx - '%s'\n", hash, prefix->hash1, str));
292 return -1;
293 }
294
295 if (prefix->hash2)
296 {
297 hash = 0;
298 ISTRING_HASH_2 (str, hash);
299 if (hash != prefix->hash2)
300 {
301 MY_ASSERT_MSG (0, ("hash2: %lx != %lx - '%s'\n", hash, prefix->hash2, str));
302 return -1;
303 }
304 }
305
306 return 0;
307}
308
309/* Fallback for when the hash2 value isn't present. */
310unsigned long
311strcache_get_hash2_fallback (const char *str)
312{
313 struct strcache_pref *prefix = (struct strcache_pref *)str - 1;
314 unsigned long hash2 = 0;
315
316 ISTRING_HASH_2 (str, hash2);
317 prefix->hash2 = hash2;
318
319 return hash2;
320}
321
322#endif /* CONFIG_WITH_VALUE_LENGTH */
323
324/* Returns true if the string is in the cache; false if not. */
325int
326strcache_iscached (const char *str)
327{
328 struct strcache *sp;
329
330 for (sp = strcache; sp != 0; sp = sp->next)
331 if (str >= sp->buffer && str < sp->end)
332 return 1;
333
334 return 0;
335}
336
337/* If the string is already in the cache, return a pointer to the cached
338 version. If not, add it then return a pointer to the cached version.
339 Note we do NOT take control of the string passed in. */
340const char *
341strcache_add (const char *str)
342{
343#ifndef CONFIG_WITH_VALUE_LENGTH
344 return add_hash (str, strlen (str));
345#else
346 /* XXX: calc the hash1 while determining the string length. */
347 return add_hash (str, strlen (str), 0, 0);
348#endif
349}
350
351const char *
352strcache_add_len (const char *str, int len)
353{
354 /* If we're not given a nul-terminated string we have to create one, because
355 the hashing functions expect it. */
356 if (str[len] != '\0')
357 {
358 char *key = alloca (len + 1);
359 memcpy (key, str, len);
360 key[len] = '\0';
361 str = key;
362 }
363
364#ifndef CONFIG_WITH_VALUE_LENGTH
365 return add_hash (str, len);
366#else
367 /* XXX: eliminate the alloca mess using the prefixing? */
368 return add_hash (str, len, 0, 0);
369#endif
370}
371
372#ifdef CONFIG_WITH_INCLUDEDEP
373
374/* A special variant used by the includedep worker threads, it off loads
375 the main thread when it adds the strings to the cache later. */
376const char *
377strcache_add_prehashed (const char *str, int len, unsigned long hash1,
378 unsigned long hash2)
379{
380 return add_hash (str, len, hash1, hash2);
381}
382
383/* Performs the prehashing for use with strcache_add_prehashed(). */
384void
385strcache_prehash_str (const char *str, unsigned int len, unsigned long *hash1p,
386 unsigned long *hash2p)
387{
388 (void)len;
389 *hash1p = str_hash_1 (str);
390 *hash2p = str_hash_2 (str);
391}
392
393#endif /* CONFIG_WITH_INCLUDEDEP */
394
395int
396strcache_setbufsize(int size)
397{
398 if (size > bufsize)
399 bufsize = size;
400 return bufsize;
401}
402
403void
404strcache_init (void)
405{
406#ifdef KMK
407 hash_init (&strings, 65535, str_hash_1, str_hash_2, str_hash_cmp);
408#else
409 hash_init (&strings, 8000, str_hash_1, str_hash_2, str_hash_cmp);
410#endif
411}
412
413
414/* Generate some stats output. */
415
416void
417strcache_print_stats (const char *prefix)
418{
419 int numbuffs = 0, numstrs = 0;
420 int totsize = 0, avgsize, maxsize = 0, minsize = bufsize;
421 int totfree = 0, avgfree, maxfree = 0, minfree = bufsize;
422 int lastused = 0, lastfree = 0;
423
424 if (strcache)
425 {
426 const struct strcache *sp;
427
428 /* Count the first buffer separately since it's not full. */
429 lastused = strcache->end - strcache->buffer;
430 lastfree = strcache->bytesfree;
431
432 for (sp = strcache->next; sp != NULL; sp = sp->next)
433 {
434 int bf = sp->bytesfree;
435 int sz = sp->end - sp->buffer;
436
437 ++numbuffs;
438 numstrs += sp->count;
439
440 totsize += sz;
441 maxsize = (sz > maxsize ? sz : maxsize);
442 minsize = (sz < minsize ? sz : minsize);
443
444 totfree += bf;
445 maxfree = (bf > maxfree ? bf : maxfree);
446 minfree = (bf < minfree ? bf : minfree);
447 }
448 }
449
450 avgsize = numbuffs ? (int)(totsize / numbuffs) : 0;
451 avgfree = numbuffs ? (int)(totfree / numbuffs) : 0;
452
453 printf (_("\n%s # of strings in strcache: %d / lookups = %lu / hits = %lu\n"),
454 prefix, numstrs, total_adds, (total_adds - numstrs));
455 printf (_("%s # of strcache buffers: %d (* %d B/buffer = %d B)\n"),
456 prefix, (numbuffs + 1), bufsize, ((numbuffs + 1) * bufsize));
457 printf (_("%s strcache used: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
458 prefix, totsize, lastused, maxsize, minsize, avgsize);
459 printf (_("%s strcache free: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
460 prefix, totfree, lastfree, maxfree, minfree, avgfree);
461
462 fputs (_("\n# strcache hash-table stats:\n# "), stdout);
463 hash_print_stats (&strings, stdout);
464}
465
466#else /* CONFIG_WITH_STRCACHE2 */
467
468#include "strcache2.h"
469
470/* The file string cache. */
471struct strcache2 file_strcache;
472
473void strcache_init (void)
474{
475 strcache2_init(&file_strcache,
476 "file", /* name */
477 65536, /* hash size */
478 0, /* default segment size*/
479#ifdef HAVE_CASE_INSENSITIVE_FS
480 1, /* case insensitive */
481#else
482 0, /* case insensitive */
483#endif
484 0); /* thread safe */
485}
486
487void
488strcache_print_stats (const char *prefix)
489{
490 strcache2_print_stats (&file_strcache, prefix);
491}
492
493
494#endif /* CONFIG_WITH_STRCACHE2 */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette