2000-03-21 J.T. Conklin <jtc@redback.com>
[platform/upstream/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   unsigned int new_num_buckets;
89   struct bstring **new_buckets;
90   unsigned int i;
91
92   /* Find the next size.  */
93   new_num_buckets = bcache->num_buckets * 2;
94   for (i = 0; i < (sizeof (sizes) / sizeof (sizes[0])); i++)
95     if (sizes[i] > bcache->num_buckets)
96       {
97         new_num_buckets = sizes[i];
98         break;
99       }
100
101   /* Allocate the new table.  */
102   {
103     size_t new_size = new_num_buckets * sizeof (new_buckets[0]);
104     new_buckets = (struct bstring **) xmalloc (new_size);
105     memset (new_buckets, 0, new_size);
106
107     bcache->structure_size -= (bcache->num_buckets
108                                * sizeof (bcache->bucket[0]));
109     bcache->structure_size += new_size;
110   }
111
112   /* Rehash all existing strings.  */
113   for (i = 0; i < bcache->num_buckets; i++)
114     {
115       struct bstring *s, *next;
116
117       for (s = bcache->bucket[i]; s; s = next)
118         {
119           struct bstring **new_bucket;
120           next = s->next;
121
122           new_bucket = &new_buckets[(hash (&s->d.data, s->length)
123                                      % new_num_buckets)];
124           s->next = *new_bucket;
125           *new_bucket = s;
126         }
127     }
128
129   /* Plug in the new table.  */
130   if (bcache->bucket)
131     free (bcache->bucket);
132   bcache->bucket = new_buckets;
133   bcache->num_buckets = new_num_buckets;
134 }
135
136 \f
137 /* Looking up things in the bcache.  */
138
139 /* The number of bytes needed to allocate a struct bstring whose data
140    is N bytes long.  */
141 #define BSTRING_SIZE(n) (offsetof (struct bstring, d.data) + (n))
142
143 /* Find a copy of the LENGTH bytes at ADDR in BCACHE.  If BCACHE has
144    never seen those bytes before, add a copy of them to BCACHE.  In
145    either case, return a pointer to BCACHE's copy of that string.  */
146 void *
147 bcache (void *addr, int length, struct bcache *bcache)
148 {
149   int hash_index;
150   struct bstring *s;
151
152   /* If our average chain length is too high, expand the hash table.  */
153   if (bcache->unique_count >= bcache->num_buckets * CHAIN_LENGTH_THRESHOLD)
154     expand_hash_table (bcache);
155
156   bcache->total_count++;
157   bcache->total_size += length;
158
159   hash_index = hash (addr, length) % bcache->num_buckets;
160
161   /* Search the hash bucket for a string identical to the caller's.  */
162   for (s = bcache->bucket[hash_index]; s; s = s->next)
163     if (s->length == length
164         && ! memcmp (&s->d.data, addr, length))
165       return &s->d.data;
166
167   /* The user's string isn't in the list.  Insert it after *ps.  */
168   {
169     struct bstring *new
170       = obstack_alloc (&bcache->cache, BSTRING_SIZE (length));
171     memcpy (&new->d.data, addr, length);
172     new->length = length;
173     new->next = bcache->bucket[hash_index];
174     bcache->bucket[hash_index] = new;
175
176     bcache->unique_count++;
177     bcache->unique_size += length;
178     bcache->structure_size += BSTRING_SIZE (length);
179
180     return &new->d.data;
181   }
182 }
183
184 \f
185 /* Freeing bcaches.  */
186
187 /* Free all the storage associated with BCACHE.  */
188 void
189 free_bcache (struct bcache *bcache)
190 {
191   obstack_free (&bcache->cache, 0);
192   free (bcache->bucket);
193
194   /* This isn't necessary, but at least the bcache is always in a
195      consistent state.  */
196   memset (bcache, 0, sizeof (*bcache));
197 }
198
199
200 \f
201 /* Printing statistics.  */
202
203 static int
204 compare_ints (const void *ap, const void *bp)
205 {
206   /* Because we know we're comparing two ints which are positive,
207      there's no danger of overflow here.  */
208   return * (int *) ap - * (int *) bp;
209 }
210
211
212 static void
213 print_percentage (int portion, int total)
214 {
215   if (total == 0)
216     printf_filtered ("(not applicable)\n");
217   else
218     printf_filtered ("%3d%%\n", portion * 100 / total);
219 }
220
221
222 /* Print statistics on BCACHE's memory usage and efficacity at
223    eliminating duplication.  NAME should describe the kind of data
224    BCACHE holds.  Statistics are printed using `printf_filtered' and
225    its ilk.  */
226 void
227 print_bcache_statistics (struct bcache *c, char *type)
228 {
229   int occupied_buckets;
230   int max_chain_length;
231   int median_chain_length;
232
233   /* Count the number of occupied buckets, and measure chain lengths.  */
234   {
235     unsigned int b;
236     int *chain_length
237       = (int *) alloca (c->num_buckets * sizeof (*chain_length));
238
239     occupied_buckets = 0;
240
241     for (b = 0; b < c->num_buckets; b++)
242       {
243         struct bstring *s = c->bucket[b];
244
245         chain_length[b] = 0;
246
247         if (s)
248           {
249             occupied_buckets++;
250             
251             while (s)
252               {
253                 chain_length[b]++;
254                 s = s->next;
255               }
256           }
257       }
258
259     /* To compute the median, we need the set of chain lengths sorted.  */
260     qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
261            compare_ints);
262
263     if (c->num_buckets > 0)
264       {
265         max_chain_length = chain_length[c->num_buckets - 1];
266         median_chain_length = chain_length[c->num_buckets / 2];
267       }
268     else
269       {
270         max_chain_length = 0;
271         median_chain_length = 0;
272       }
273   }
274
275   printf_filtered ("  Cached '%s' statistics:\n", type);
276   printf_filtered ("    Total object count:  %ld\n", c->total_count);
277   printf_filtered ("    Unique object count: %lu\n", c->unique_count);
278   printf_filtered ("    Percentage of duplicates, by count: ");
279   print_percentage (c->total_count - c->unique_count, c->total_count);
280   printf_filtered ("\n");
281
282   printf_filtered ("    Total object size:   %ld\n", c->total_size);
283   printf_filtered ("    Unique object size:  %ld\n", c->unique_size);
284   printf_filtered ("    Percentage of duplicates, by size:  ");
285   print_percentage (c->total_size - c->unique_size, c->total_size);
286   printf_filtered ("\n");
287
288   printf_filtered ("    Total memory used by bcache, including overhead: %ld\n",
289                    c->structure_size);
290   printf_filtered ("    Percentage memory overhead: ");
291   print_percentage (c->structure_size - c->unique_size, c->unique_size);
292   printf_filtered ("    Net memory savings:         ");
293   print_percentage (c->total_size - c->structure_size, c->total_size);
294   printf_filtered ("\n");
295
296   printf_filtered ("    Hash table size:           %3d\n", c->num_buckets);
297   printf_filtered ("    Hash table population:     ");
298   print_percentage (occupied_buckets, c->num_buckets);
299   printf_filtered ("    Median hash chain length:  %3d\n",
300                    median_chain_length);
301   printf_filtered ("    Average hash chain length: ");
302   if (c->num_buckets > 0)
303     printf_filtered ("%3lu\n", c->unique_count / c->num_buckets);
304   else
305     printf_filtered ("(not applicable)\n");
306   printf_filtered ("    Maximum hash chain length: %3d\n", max_chain_length);
307   printf_filtered ("\n");
308 }