import gdb-1999-12-06 snapshot
[external/binutils.git] / gdb / bcache.c
1 /* Implement a cached obstack.
2    Written by Fred Fish <fnf@cygnus.com>
3    Rewritten by Jim Blandy <jimb@cygnus.com>
4    Copyright 1999 Free Software Foundation, Inc.
5
6    This file is part of GDB.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330,
21    Boston, MA 02111-1307, USA.  */
22
23 #include <stddef.h>
24 #include <stdlib.h>
25
26 #include "defs.h"
27 #include "obstack.h"
28 #include "bcache.h"
29 #include "gdb_string.h"         /* For memcpy declaration */
30
31
32 \f
33 /* The hash function.  */
34
35 unsigned long
36 hash (void *addr, int length)
37 {
38   /* If it's a short string, hash on every character.  Otherwise, sample
39      characters from throughout the string.  */
40   if (length <= 64)
41     {
42       char *byte = addr;
43       unsigned long h = 0;
44       int i;
45
46       for (i = 0; i < length; i++)
47         h = h * 65793 ^ (h >> (sizeof (h) * 8 - 6)) ^ byte[i];
48
49       return h;
50     }
51   else
52     {
53       char *byte = addr;
54       int n, i;
55       unsigned long h = 0;
56
57       for (n = i = 0; n < 64; n++)
58         {
59           h = h * 65793 + (h >> (sizeof (h) * 8 - 6)) + byte[i];
60           i = h % length;
61         }
62
63       return h;
64     }
65 }
66
67 \f
68 /* Growing the bcache's hash table.  */
69
70 /* If the average chain length grows beyond this, then we want to
71    resize our hash table.  */
72 #define CHAIN_LENGTH_THRESHOLD (5)
73
74 static void
75 expand_hash_table (struct bcache *bcache)
76 {
77   /* A table of good hash table sizes.  Whenever we grow, we pick the
78      next larger size from this table.  sizes[i] is close to 1 << (i+10),
79      so we roughly double the table size each time.  After we fall off 
80      the end of this table, we just double.  Don't laugh --- there have
81      been executables sighted with a gigabyte of debug info.  */
82   static unsigned long sizes[] = { 
83     1021, 2053, 4099, 8191, 16381, 32771,
84     65537, 131071, 262144, 524287, 1048573, 2097143,
85     4194301, 8388617, 16777213, 33554467, 67108859, 134217757,
86     268435459, 536870923, 1073741827, 2147483659UL
87   };
88   int new_num_buckets;
89   struct bstring **new_buckets;
90   int i;
91
92   /* Find the next size.  */
93   for (i = 0; i < (sizeof (sizes) / sizeof (sizes[0])); i++)
94     if (sizes[i] > bcache->num_buckets)
95       {
96         new_num_buckets = sizes[i];
97         break;
98       }
99   if (i >= (sizeof (sizes) / sizeof (sizes[0])))
100     new_num_buckets = bcache->num_buckets * 2;
101
102   /* Allocate the new table.  */
103   {
104     size_t new_size = new_num_buckets * sizeof (new_buckets[0]);
105     new_buckets = (struct bstring **) xmalloc (new_size);
106     memset (new_buckets, 0, new_size);
107
108     bcache->structure_size -= (bcache->num_buckets
109                                * sizeof (bcache->bucket[0]));
110     bcache->structure_size += new_size;
111   }
112
113   /* Rehash all existing strings.  */
114   for (i = 0; i < bcache->num_buckets; i++)
115     {
116       struct bstring *s, *next;
117
118       for (s = bcache->bucket[i]; s; s = next)
119         {
120           struct bstring **new_bucket;
121           next = s->next;
122
123           new_bucket = &new_buckets[(hash (&s->d.data, s->length)
124                                      % new_num_buckets)];
125           s->next = *new_bucket;
126           *new_bucket = s;
127         }
128     }
129
130   /* Plug in the new table.  */
131   if (bcache->bucket)
132     free (bcache->bucket);
133   bcache->bucket = new_buckets;
134   bcache->num_buckets = new_num_buckets;
135 }
136
137 \f
138 /* Looking up things in the bcache.  */
139
140 /* The number of bytes needed to allocate a struct bstring whose data
141    is N bytes long.  */
142 #define BSTRING_SIZE(n) (offsetof (struct bstring, d.data) + (n))
143
144 /* Find a copy of the LENGTH bytes at ADDR in BCACHE.  If BCACHE has
145    never seen those bytes before, add a copy of them to BCACHE.  In
146    either case, return a pointer to BCACHE's copy of that string.  */
147 void *
148 bcache (void *addr, int length, struct bcache *bcache)
149 {
150   int hash_index;
151   struct bstring *s;
152
153   /* If our average chain length is too high, expand the hash table.  */
154   if (bcache->unique_count >= bcache->num_buckets * CHAIN_LENGTH_THRESHOLD)
155     expand_hash_table (bcache);
156
157   bcache->total_count++;
158   bcache->total_size += length;
159
160   hash_index = hash (addr, length) % bcache->num_buckets;
161
162   /* Search the hash bucket for a string identical to the caller's.  */
163   for (s = bcache->bucket[hash_index]; s; s = s->next)
164     if (s->length == length
165         && ! memcmp (&s->d.data, addr, length))
166       return &s->d.data;
167
168   /* The user's string isn't in the list.  Insert it after *ps.  */
169   {
170     struct bstring *new
171       = obstack_alloc (&bcache->cache, BSTRING_SIZE (length));
172     memcpy (&new->d.data, addr, length);
173     new->length = length;
174     new->next = bcache->bucket[hash_index];
175     bcache->bucket[hash_index] = new;
176
177     bcache->unique_count++;
178     bcache->unique_size += length;
179     bcache->structure_size += BSTRING_SIZE (length);
180
181     return &new->d.data;
182   }
183 }
184
185 \f
186 /* Freeing bcaches.  */
187
188 /* Free all the storage associated with BCACHE.  */
189 void
190 free_bcache (struct bcache *bcache)
191 {
192   obstack_free (&bcache->cache, 0);
193   free (bcache->bucket);
194
195   /* This isn't necessary, but at least the bcache is always in a
196      consistent state.  */
197   memset (bcache, 0, sizeof (*bcache));
198 }
199
200
201 \f
202 /* Printing statistics.  */
203
204 static int
205 compare_ints (const void *ap, const void *bp)
206 {
207   /* Because we know we're comparing two ints which are positive,
208      there's no danger of overflow here.  */
209   return * (int *) ap - * (int *) bp;
210 }
211
212
213 static void
214 print_percentage (int portion, int total)
215 {
216   if (total == 0)
217     printf_filtered ("(not applicable)\n");
218   else
219     printf_filtered ("%3d%%\n", portion * 100 / total);
220 }
221
222
223 /* Print statistics on BCACHE's memory usage and efficacity at
224    eliminating duplication.  NAME should describe the kind of data
225    BCACHE holds.  Statistics are printed using `printf_filtered' and
226    its ilk.  */
227 void
228 print_bcache_statistics (struct bcache *c, char *type)
229 {
230   int occupied_buckets;
231   int max_chain_length;
232   int median_chain_length;
233
234   /* Count the number of occupied buckets, and measure chain lengths.  */
235   {
236     int b;
237     int *chain_length
238       = (int *) alloca (c->num_buckets * sizeof (*chain_length));
239
240     occupied_buckets = 0;
241
242     for (b = 0; b < c->num_buckets; b++)
243       {
244         struct bstring *s = c->bucket[b];
245
246         chain_length[b] = 0;
247
248         if (s)
249           {
250             occupied_buckets++;
251             
252             while (s)
253               {
254                 chain_length[b]++;
255                 s = s->next;
256               }
257           }
258       }
259
260     /* To compute the median, we need the set of chain lengths sorted.  */
261     qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
262            compare_ints);
263
264     if (c->num_buckets > 0)
265       {
266         max_chain_length = chain_length[c->num_buckets - 1];
267         median_chain_length = chain_length[c->num_buckets / 2];
268       }
269     else
270       {
271         max_chain_length = 0;
272         median_chain_length = 0;
273       }
274   }
275
276   printf_filtered ("  Cached '%s' statistics:\n", type);
277   printf_filtered ("    Total object count:  %ld\n", c->total_count);
278   printf_filtered ("    Unique object count: %ld\n", c->unique_count);
279   printf_filtered ("    Percentage of duplicates, by count: ");
280   print_percentage (c->total_count - c->unique_count, c->total_count);
281   printf_filtered ("\n");
282
283   printf_filtered ("    Total object size:   %ld\n", c->total_size);
284   printf_filtered ("    Unique object size:  %ld\n", c->unique_size);
285   printf_filtered ("    Percentage of duplicates, by size:  ");
286   print_percentage (c->total_size - c->unique_size, c->total_size);
287   printf_filtered ("\n");
288
289   printf_filtered ("    Total memory used by bcache, including overhead: %ld\n",
290                    c->structure_size);
291   printf_filtered ("    Percentage memory overhead: ");
292   print_percentage (c->structure_size - c->unique_size, c->unique_size);
293   printf_filtered ("    Net memory savings:         ");
294   print_percentage (c->total_size - c->structure_size, c->total_size);
295   printf_filtered ("\n");
296
297   printf_filtered ("    Hash table size:           %3d\n", c->num_buckets);
298   printf_filtered ("    Hash table population:     ");
299   print_percentage (occupied_buckets, c->num_buckets);
300   printf_filtered ("    Median hash chain length:  %3d\n",
301                    median_chain_length);
302   printf_filtered ("    Average hash chain length: ");
303   if (c->num_buckets > 0)
304     printf_filtered ("%3ld\n", c->unique_count / c->num_buckets);
305   else
306     printf_filtered ("(not applicable)\n");
307   printf_filtered ("    Maximum hash chain length: %3d\n", max_chain_length);
308   printf_filtered ("\n");
309 }