source: tags/svn.1.5.4/ARBDB/adcache.cxx

Last change on this file was 8313, checked in by westram, 14 years ago
  • removed dead code

(partly reverted by [8316])

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.2 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adcache.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_storage.h"
12
13struct gb_cache_entry {
14    GBDATA       *gbd;
15    gb_cache_idx  prev;
16    gb_cache_idx  next;
17    char         *data;
18    long          clock;
19    size_t        sizeof_data;
20
21#if defined(GEN_CACHE_STATS)
22    long  reused;
23    char *dbpath;
24#endif // GEN_CACHE_STATS
25};
26
27inline bool entry_is_linked(gb_cache& cache, gb_cache_idx index) {
28    gb_cache_entry& entry = cache.entries[index];
29    return
30        (entry.prev || cache.newest_entry == index) &&
31        (entry.next || cache.oldest_entry == index);
32}
33
34inline gb_cache_entry& unlink_cache_entry(gb_cache& cache, gb_cache_idx index) {
35    gb_assert(entry_is_linked(cache, index));
36
37    gb_cache_entry& entry = cache.entries[index];
38
39    gb_cache_idx p = entry.prev;
40    gb_cache_idx n = entry.next;
41
42    if (index == cache.newest_entry) cache.newest_entry = n;
43    if (index == cache.oldest_entry) cache.oldest_entry = p;
44
45    cache.entries[n].prev = p;
46    cache.entries[p].next = n;
47
48    entry.prev = entry.next = 0;
49
50    return entry;
51}
52
53inline void link_cache_entry_to_top(gb_cache& cache, gb_cache_idx index) {
54    gb_assert(!entry_is_linked(cache, index));
55
56    gb_cache_entry& entry = cache.entries[index];
57
58    entry.prev = entry.next = 0;
59   
60    if (!cache.newest_entry) {                      // first entry
61        gb_assert(!cache.oldest_entry);
62        cache.newest_entry = cache.oldest_entry = index;
63    }
64    else if (entry.sizeof_data >= cache.big_data_min_size) {
65        // Do NOT put big entries to top - instead put them to bottom!
66        // This is done to avoid that reading a big entry flushes the complete cache.
67        entry.prev                     = cache.oldest_entry;
68        cache.entries[entry.prev].next = index;
69        cache.oldest_entry             = index;
70    }
71    else {
72        entry.next                     = cache.newest_entry;
73        cache.entries[entry.next].prev = index;
74        cache.newest_entry             = index;
75    }
76}
77
78inline void flush_cache_entry(gb_cache& cache, gb_cache_idx index) {
79    gb_assert(!entry_is_linked(cache, index));
80   
81    gb_cache_entry& entry = cache.entries[index];
82
83    freenull(entry.data);
84    cache.sum_data_size    -= entry.sizeof_data;
85    gb_assert(entry.gbd->cache_index == index); // oops - cache error
86    entry.gbd->cache_index  = 0;
87
88    // insert deleted entry in free list
89    entry.next            = cache.firstfree_entry;
90    cache.firstfree_entry = index;
91
92#if defined(GEN_CACHE_STATS)
93    // const char *dbpath = GB_get_db_path(entry.gbd);
94    if (entry.reused) {
95        GBS_incr_hash(cache.reused, entry.dbpath);
96        GBS_write_hash(cache.reuse_sum, entry.dbpath, GBS_read_hash(cache.reuse_sum, entry.dbpath)+entry.reused);
97    }
98    else {
99        GBS_incr_hash(cache.not_reused, entry.dbpath);
100    }
101    freenull(entry.dbpath);
102#endif // GEN_CACHE_STATS
103}
104
105void gb_init_cache(GB_MAIN_TYPE *Main) {
106    gb_cache& cache = Main->cache;
107
108    if (!cache.entries) {
109        cache.entries = (gb_cache_entry *)GB_calloc(sizeof(gb_cache_entry), GB_MAX_CACHED_ENTRIES);
110
111        cache.max_entries       = GB_MAX_CACHED_ENTRIES;
112        cache.max_data_size     = GB_TOTAL_CACHE_SIZE;
113        cache.big_data_min_size = cache.max_data_size / 4;
114
115        for (gb_cache_idx i=0; i<GB_MAX_CACHED_ENTRIES-1; i++) {
116            cache.entries[i].next = i+1;
117        }
118        cache.firstfree_entry = 1;
119
120#if defined(GEN_CACHE_STATS)
121        cache.not_reused = GBS_create_hash(1000, GB_MIND_CASE);
122        cache.reused     = GBS_create_hash(1000, GB_MIND_CASE);
123        cache.reuse_sum  = GBS_create_hash(1000, GB_MIND_CASE);
124#endif // GEN_CACHE_STATS
125    }
126}
127
128#if defined(GEN_CACHE_STATS)
129static long sum_hash_values(const char */*key*/, long val, void *client_data) {
130    size_t *sum  = (size_t*)client_data;
131    *sum      += val;
132    return val;
133}
134static long list_hash_entries(const char *key, long val, void *client_data) {
135    if (client_data) {
136        GB_HASH *reuse_sum_hash = (GB_HASH*)client_data;
137        long     reuse_sum      = GBS_read_hash(reuse_sum_hash, key);
138
139        printf("%s %li (%5.2f)\n", key, val, (double)reuse_sum/val);
140    }
141    else {
142        printf("%s %li\n", key, val);
143    }
144    return val;
145}
146#endif // GEN_CACHE_STATS
147
148void gb_destroy_cache(GB_MAIN_TYPE *Main) {
149    // only call this from gb_destroy_main()!
150    gb_cache& cache = Main->cache;
151
152    if (cache.entries) {
153        gb_assert(cache.newest_entry == 0);         // cache is not flushed
154        gb_assert(cache.sum_data_size == 0);
155        freenull(cache.entries);
156
157#if defined(GEN_CACHE_STATS)
158        size_t not_reused = 0;
159        size_t reused     = 0;
160        size_t reuse_sum  = 0;
161
162        GBS_hash_do_loop(cache.reuse_sum, sum_hash_values, &reuse_sum);
163        GBS_hash_do_loop(cache.not_reused, sum_hash_values, &not_reused);
164        GBS_hash_do_loop(cache.reused, sum_hash_values, &reused);
165
166        size_t overall = not_reused+reused;
167
168        printf("Cache stats:\n"
169               "Overall entries:  %zu\n"
170               "Reused entries:   %zu (%5.2f%%)\n"
171               "Mean reuse count: %5.2f\n",
172               overall,
173               reused, (double)reused/overall*100.0,
174               (double)reuse_sum/reused);
175
176        printf("Not reused:\n");
177        GBS_hash_do_sorted_loop(cache.not_reused, list_hash_entries, GBS_HCF_sortedByKey, NULL);
178        printf("Reused:\n");
179        GBS_hash_do_sorted_loop(cache.reused, list_hash_entries, GBS_HCF_sortedByKey, cache.reuse_sum);
180
181        GBS_free_hash(cache.not_reused);
182        GBS_free_hash(cache.reused);
183        GBS_free_hash(cache.reuse_sum);
184#endif // GEN_CACHE_STATS
185    }
186}
187
188char *gb_read_cache(GBDATA *gbd) {
189    char         *cached_data = NULL;
190    gb_cache_idx  index       = gbd->cache_index;
191
192    if (index) {
193        gb_cache&       cache = GB_MAIN(gbd)->cache;
194        gb_cache_entry& entry = unlink_cache_entry(cache, index);
195        gb_assert(entry.gbd == gbd);
196
197        // check validity
198        if (GB_GET_EXT_UPDATE_DATE(gbd) > entry.clock) {
199            flush_cache_entry(cache, index);
200        }
201        else {
202            link_cache_entry_to_top(cache, index);
203            cached_data = entry.data;
204#if defined(GEN_CACHE_STATS)
205            entry.reused++;
206#endif // GEN_CACHE_STATS
207        }
208    }
209
210    return cached_data;
211}
212
213void gb_free_cache(GB_MAIN_TYPE *Main, GBDATA *gbd) {
214    gb_cache_idx index = gbd->cache_index;
215
216    if (index) {
217        gb_cache& cache = Main->cache;
218        unlink_cache_entry(cache, index);
219        flush_cache_entry(cache, index);
220    }
221}
222
223static char *cache_free_some_memory(gb_cache& cache, size_t needed_mem) {
224    // free up cache entries until
225    // - at least 'needed_mem' bytes are available and
226    // - at least one free cache entry exists
227    // (if one of the free'd entries has exactly 'needed_mem' bytes size,
228    // it will be returned and can be re-used or has to be freed)
229
230    long  avail_mem    = (long)cache.max_data_size - (long)cache.sum_data_size; // may be negative!
231    long  need_to_free = needed_mem-avail_mem;
232    char *data         = NULL;
233
234    // ignore really big requests (such cache entries will
235    // be appended to end of cache list and flushed quickly)
236    if (need_to_free>(long)cache.sum_data_size) need_to_free = 0;
237
238    while ((!cache.firstfree_entry || need_to_free>0) && cache.oldest_entry) {
239        gb_cache_idx index = cache.oldest_entry;
240        gb_assert(index);
241
242        gb_cache_entry& entry = unlink_cache_entry(cache, index);
243
244        need_to_free -= entry.sizeof_data;
245        if (entry.sizeof_data == needed_mem) reassign(data, entry.data);
246        flush_cache_entry(cache, index);
247    }
248
249    return data;
250}
251
252char *gb_alloc_cache_index(GBDATA *gbd, size_t size) {
253    gb_assert(gbd->cache_index == 0);
254
255    gb_cache&     cache = GB_MAIN(gbd)->cache;
256    char         *data  = cache_free_some_memory(cache, size);
257    gb_cache_idx  index = cache.firstfree_entry;
258
259    gb_assert(index);
260    gb_cache_entry& entry = cache.entries[index];
261
262    cache.firstfree_entry = entry.next;             // remove free element from free-list
263    entry.next            = 0;
264
265    // create data
266    if (!data) data = (char*)malloc(size);
267
268    entry.sizeof_data = size;
269    entry.data        = data;
270    entry.gbd         = gbd;
271    entry.clock       = GB_GET_EXT_UPDATE_DATE(gbd);
272   
273#if defined(GEN_CACHE_STATS)
274    entry.reused     = 0;
275    entry.dbpath     = strdup(GB_get_db_path(gbd));
276#endif                                              // GEN_CACHE_STATS
277
278    gbd->cache_index = index;
279
280    link_cache_entry_to_top(cache, index);
281    cache.sum_data_size += size; 
282
283        return data;
284}
285
286char *GB_set_cache_size(GBDATA *gbd, size_t size) {
287    gb_cache& cache = GB_MAIN(gbd)->cache;
288
289    cache.max_data_size     = size;
290    cache.big_data_min_size = cache.max_data_size / 4;
291    return 0;
292}
293
Note: See TracBrowser for help on using the repository browser.