VirtualBox

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

Last change on this file since 3387 was 3140, checked in by bird, 6 years ago

kmk: Merged in changes from GNU make 4.2.1 (2e55f5e4abdc0e38c1d64be703b446695e70b3b6 / https://git.savannah.gnu.org/git/make.git).

  • Property svn:eol-style set to native
File size: 10.1 KB
Line 
1/* Constant string caching for GNU Make.
2Copyright (C) 2006-2016 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 3 of the License, or (at your option) any later
8version.
9
10GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#include "makeint.h"
18#ifndef CONFIG_WITH_STRCACHE2
19
20#include <stddef.h>
21#include <assert.h>
22
23#include "hash.h"
24
25/* A string cached here will never be freed, so we don't need to worry about
26 reference counting. We just store the string, and then remember it in a
27 hash so it can be looked up again. */
28
29typedef unsigned short int sc_buflen_t;
30
31struct strcache {
32 struct strcache *next; /* The next block of strings. Must be first! */
33 sc_buflen_t end; /* Offset to the beginning of free space. */
34 sc_buflen_t bytesfree; /* Free space left in this buffer. */
35 sc_buflen_t count; /* # of strings in this buffer (for stats). */
36 char buffer[1]; /* The buffer comes after this. */
37};
38
39/* The size (in bytes) of each cache buffer.
40 Try to pick something that will map well into the heap.
41 This must be able to be represented by a short int (<=65535). */
42#define CACHE_BUFFER_BASE (8192)
43#define CACHE_BUFFER_ALLOC(_s) ((_s) - (2 * sizeof (size_t)))
44#define CACHE_BUFFER_OFFSET (offsetof (struct strcache, buffer))
45#define CACHE_BUFFER_SIZE(_s) (CACHE_BUFFER_ALLOC(_s) - CACHE_BUFFER_OFFSET)
46#define BUFSIZE CACHE_BUFFER_SIZE (CACHE_BUFFER_BASE)
47
48static struct strcache *strcache = NULL;
49static struct strcache *fullcache = NULL;
50
51static unsigned long total_buffers = 0;
52static unsigned long total_strings = 0;
53static unsigned long total_size = 0;
54
55/* Add a new buffer to the cache. Add it at the front to reduce search time.
56 This can also increase the overhead, since it's less likely that older
57 buffers will be filled in. However, GNU make has so many smaller strings
58 that this doesn't seem to be much of an issue in practice.
59 */
60static struct strcache *
61new_cache (struct strcache **head, sc_buflen_t buflen)
62{
63 struct strcache *new = xmalloc (buflen + CACHE_BUFFER_OFFSET);
64 new->end = 0;
65 new->count = 0;
66 new->bytesfree = buflen;
67
68 new->next = *head;
69 *head = new;
70
71 ++total_buffers;
72 return new;
73}
74
75static const char *
76copy_string (struct strcache *sp, const char *str, unsigned int len)
77{
78 /* Add the string to this cache. */
79 char *res = &sp->buffer[sp->end];
80
81 memmove (res, str, len);
82 res[len++] = '\0';
83 sp->end += len;
84 sp->bytesfree -= len;
85 ++sp->count;
86
87 return res;
88}
89
90static const char *
91add_string (const char *str, unsigned int len)
92{
93 const char *res;
94 struct strcache *sp;
95 struct strcache **spp = &strcache;
96 /* We need space for the nul char. */
97 unsigned int sz = len + 1;
98
99 ++total_strings;
100 total_size += sz;
101
102 /* If the string we want is too large to fit into a single buffer, then
103 no existing cache is large enough. Add it directly to the fullcache. */
104 if (sz > BUFSIZE)
105 {
106 sp = new_cache (&fullcache, sz);
107 return copy_string (sp, str, len);
108 }
109
110 /* Find the first cache with enough free space. */
111 for (; *spp != NULL; spp = &(*spp)->next)
112 if ((*spp)->bytesfree > sz)
113 break;
114 sp = *spp;
115
116 /* If nothing is big enough, make a new cache at the front. */
117 if (sp == NULL)
118 {
119 sp = new_cache (&strcache, BUFSIZE);
120 spp = &strcache;
121 }
122
123 /* Add the string to this cache. */
124 res = copy_string (sp, str, len);
125
126 /* If the amount free in this cache is less than the average string size,
127 consider it full and move it to the full list. */
128 if (total_strings > 20 && sp->bytesfree < (total_size / total_strings) + 1)
129 {
130 *spp = sp->next;
131 sp->next = fullcache;
132 fullcache = sp;
133 }
134
135 return res;
136}
137
138/* For strings too large for the strcache, we just save them in a list. */
139struct hugestring {
140 struct hugestring *next; /* The next string. */
141 char buffer[1]; /* The string. */
142};
143
144static struct hugestring *hugestrings = NULL;
145
146static const char *
147add_hugestring (const char *str, unsigned int len)
148{
149 struct hugestring *new = xmalloc (sizeof (struct hugestring) + len);
150 memcpy (new->buffer, str, len);
151 new->buffer[len] = '\0';
152
153 new->next = hugestrings;
154 hugestrings = new;
155
156 return new->buffer;
157}
158
159/* Hash table of strings in the cache. */
160
161static unsigned long
162str_hash_1 (const void *key)
163{
164 return_ISTRING_HASH_1 ((const char *) key);
165}
166
167static unsigned long
168str_hash_2 (const void *key)
169{
170 return_ISTRING_HASH_2 ((const char *) key);
171}
172
173static int
174str_hash_cmp (const void *x, const void *y)
175{
176 return_ISTRING_COMPARE ((const char *) x, (const char *) y);
177}
178
179static struct hash_table strings;
180static unsigned long total_adds = 0;
181
182static const char *
183add_hash (const char *str, unsigned int len)
184{
185 char *const *slot;
186 const char *key;
187
188 /* If it's too large for the string cache, just copy it.
189 We don't bother trying to match these. */
190 if (len > USHRT_MAX - 1)
191 return add_hugestring (str, len);
192
193 /* Look up the string in the hash. If it's there, return it. */
194 slot = (char *const *) hash_find_slot (&strings, str);
195 key = *slot;
196
197 /* Count the total number of add operations we performed. */
198 ++total_adds;
199
200 if (!HASH_VACANT (key))
201 return key;
202
203 /* Not there yet so add it to a buffer, then into the hash table. */
204 key = add_string (str, len);
205 hash_insert_at (&strings, key, slot);
206 return key;
207}
208
209/* Returns true if the string is in the cache; false if not. */
210int
211strcache_iscached (const char *str)
212{
213 struct strcache *sp;
214
215 for (sp = strcache; sp != 0; sp = sp->next)
216 if (str >= sp->buffer && str < sp->buffer + sp->end)
217 return 1;
218 for (sp = fullcache; sp != 0; sp = sp->next)
219 if (str >= sp->buffer && str < sp->buffer + sp->end)
220 return 1;
221
222 {
223 struct hugestring *hp;
224 for (hp = hugestrings; hp != 0; hp = hp->next)
225 if (str == hp->buffer)
226 return 1;
227 }
228
229 return 0;
230}
231
232/* If the string is already in the cache, return a pointer to the cached
233 version. If not, add it then return a pointer to the cached version.
234 Note we do NOT take control of the string passed in. */
235const char *
236strcache_add (const char *str)
237{
238 return add_hash (str, strlen (str));
239}
240
241const char *
242strcache_add_len (const char *str, unsigned int len)
243{
244 /* If we're not given a nul-terminated string we have to create one, because
245 the hashing functions expect it. */
246 if (str[len] != '\0')
247 {
248 char *key = alloca (len + 1);
249 memcpy (key, str, len);
250 key[len] = '\0';
251 str = key;
252 }
253
254 return add_hash (str, len);
255}
256
257void
258strcache_init (void)
259{
260 hash_init (&strings, 8000, str_hash_1, str_hash_2, str_hash_cmp);
261}
262
263
264/* Generate some stats output. */
265
266void
267strcache_print_stats (const char *prefix)
268{
269 const struct strcache *sp;
270 unsigned long numbuffs = 0, fullbuffs = 0;
271 unsigned long totfree = 0, maxfree = 0, minfree = BUFSIZE;
272
273 if (! strcache)
274 {
275 printf (_("\n%s No strcache buffers\n"), prefix);
276 return;
277 }
278
279 /* Count the first buffer separately since it's not full. */
280 for (sp = strcache->next; sp != NULL; sp = sp->next)
281 {
282 sc_buflen_t bf = sp->bytesfree;
283
284 totfree += bf;
285 maxfree = (bf > maxfree ? bf : maxfree);
286 minfree = (bf < minfree ? bf : minfree);
287
288 ++numbuffs;
289 }
290 for (sp = fullcache; sp != NULL; sp = sp->next)
291 {
292 sc_buflen_t bf = sp->bytesfree;
293
294 totfree += bf;
295 maxfree = (bf > maxfree ? bf : maxfree);
296 minfree = (bf < minfree ? bf : minfree);
297
298 ++numbuffs;
299 ++fullbuffs;
300 }
301
302 /* Make sure we didn't lose any buffers. */
303 assert (total_buffers == numbuffs + 1);
304
305 printf (_("\n%s strcache buffers: %lu (%lu) / strings = %lu / storage = %lu B / avg = %lu B\n"),
306 prefix, numbuffs + 1, fullbuffs, total_strings, total_size,
307 (total_size / total_strings));
308
309 printf (_("%s current buf: size = %hu B / used = %hu B / count = %hu / avg = %hu B\n"),
310 prefix, (sc_buflen_t)BUFSIZE, strcache->end, strcache->count,
311 (strcache->end / strcache->count));
312
313 if (numbuffs)
314 {
315 /* Show information about non-current buffers. */
316 unsigned long sz = total_size - strcache->end;
317 unsigned long cnt = total_strings - strcache->count;
318 sc_buflen_t avgfree = totfree / numbuffs;
319
320 printf (_("%s other used: total = %lu B / count = %lu / avg = %lu B\n"),
321 prefix, sz, cnt, sz / cnt);
322
323 printf (_("%s other free: total = %lu B / max = %lu B / min = %lu B / avg = %hu B\n"),
324 prefix, totfree, maxfree, minfree, avgfree);
325 }
326
327 printf (_("\n%s strcache performance: lookups = %lu / hit rate = %lu%%\n"),
328 prefix, total_adds, (long unsigned)(100.0 * (total_adds - total_strings) / total_adds));
329 fputs (_("# hash-table stats:\n# "), stdout);
330 hash_print_stats (&strings, stdout);
331}
332
333#else /* CONFIG_WITH_STRCACHE2 */
334
335#include "strcache2.h"
336
337const char *suffixes_strcached;
338
339/* The file string cache. */
340struct strcache2 file_strcache;
341
342void strcache_init (void)
343{
344 strcache2_init(&file_strcache,
345 "file", /* name */
346 131072, /* hash size */
347 0, /* default segment size*/
348#ifdef HAVE_CASE_INSENSITIVE_FS
349 1, /* case insensitive */
350#else
351 0, /* case insensitive */
352#endif
353 0); /* thread safe */
354
355 /* .SUFFIXES is referenced in several loops, keep the added pointer in a
356 global var so these can be optimized. */
357
358 suffixes_strcached = strcache_add_len (".SUFFIXES", sizeof (".SUFFIXES")-1);
359}
360
361void
362strcache_print_stats (const char *prefix)
363{
364 strcache2_print_stats (&file_strcache, prefix);
365}
366
367#endif /* CONFIG_WITH_STRCACHE2 */
368
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use