Add OALLOC*() to make allocating from obstacks a bit nicer.
[libfirm] / ir / stat / distrib.c
1 /*
2  * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19
20 /**
21  * @file
22  * @brief   Statistics for Firm. Distribution tables.
23  * @author  Michael Beck
24  * @version $Id$
25  */
26 #include "config.h"
27
28 #include "hashptr.h"
29 #include "irtools.h"
30 #include "xmalloc.h"
31 #include "firmstat_t.h"
32
33 /**
34  * calculates a hash value for an address
35  */
36 static unsigned addr_hash(const void *object) {
37         return HASH_PTR(object);
38 }
39
40 /**
41  * calculates a hash value for an integer
42  */
43 static unsigned int_hash(const void *object) {
44         return (unsigned)PTR_TO_INT(object);
45 }
46
47 /**
48  * compare function for integer distribution tables
49  */
50 static int int_cmp_fun(const void *elt, const void *key) {
51         const distrib_entry_t *p1 = elt;
52         const distrib_entry_t *p2 = key;
53
54         return (char *)p1->object - (char *)p2->object;
55 }
56
57 /*
58  * create a new distribution table
59  */
60 distrib_tbl_t *stat_new_distrib_tbl(pset_cmp_fun cmp_func, distrib_hash_fun hash_func) {
61         distrib_tbl_t *res = XMALLOC(distrib_tbl_t);
62
63         obstack_init(&res->cnts);
64
65         /* create the hash-table */
66         res->hash_map  = new_pset(cmp_func, 8);
67         res->hash_func = hash_func ? hash_func : addr_hash;
68         res->int_dist  = 0;
69
70         return res;
71 }
72
73 /*
74  * create a new distribution table for an integer distribution
75  */
76 distrib_tbl_t *stat_new_int_distrib_tbl(void) {
77         distrib_tbl_t *res = stat_new_distrib_tbl(int_cmp_fun, int_hash);
78
79         if (res)
80                 res->int_dist = 1;
81
82         return res;
83 }
84
85 /*
86  * destroy a distribution table
87  */
88 void stat_delete_distrib_tbl(distrib_tbl_t *tbl) {
89         if (tbl) {
90                 /* free all entries */
91                 obstack_free(&tbl->cnts, NULL);
92
93                 /* delete the hash table */
94                 del_pset(tbl->hash_map);
95         }
96 }
97
98 /**
99  * Returns the associates distrib_entry_t for an object
100  */
101 static distrib_entry_t *distrib_get_entry(distrib_tbl_t *tbl, const void *object) {
102         distrib_entry_t key;
103         distrib_entry_t *elem;
104
105         key.object = object;
106
107         elem = pset_find(tbl->hash_map, &key, tbl->hash_func(object));
108         if (elem)
109                 return elem;
110
111         elem = OALLOC(&tbl->cnts, distrib_entry_t);
112
113         /* clear counter */
114         cnt_clr(&elem->cnt);
115
116         elem->object = object;
117
118         return pset_insert(tbl->hash_map, elem, tbl->hash_func(object));
119 }
120
121 /*
122  * adds a new object count into the distribution table
123  */
124 void stat_add_distrib_tbl(distrib_tbl_t *tbl, const void *object, const counter_t *cnt) {
125         distrib_entry_t *elem = distrib_get_entry(tbl, object);
126
127         cnt_add(&elem->cnt, cnt);
128 }
129
130 /*
131  * adds a new key count into the integer distribution table
132  */
133 void stat_add_int_distrib_tbl(distrib_tbl_t *tbl, int key, const counter_t *cnt) {
134         stat_add_distrib_tbl(tbl, INT_TO_PTR(key), cnt);
135 }
136
137 /*
138  * increases object count by one
139  */
140 void stat_inc_distrib_tbl(distrib_tbl_t *tbl, const void *object) {
141         distrib_entry_t *elem = distrib_get_entry(tbl, object);
142
143         cnt_inc(&elem->cnt);
144 }
145
146 /*
147  * increases key count by one
148  */
149 void stat_inc_int_distrib_tbl(distrib_tbl_t *tbl, int key) {
150         stat_inc_distrib_tbl(tbl, INT_TO_PTR(key));
151 }
152
153 /*
154  * inserts a new object with count 0 into the distribution table
155  * if object is already present, nothing happens
156  */
157 void stat_insert_distrib_tbl(distrib_tbl_t *tbl, const void *object) {
158         /* executed for side effect */
159         (void)distrib_get_entry(tbl, object);
160 }
161
162 /*
163  * inserts a new key with count 0 into the integer distribution table
164  * if key is already present, nothing happens
165  */
166 void stat_insert_int_distrib_tbl(distrib_tbl_t *tbl, int key) {
167         stat_insert_distrib_tbl(tbl, INT_TO_PTR(key));
168 }
169
170 /*
171  * returns the sum over all counters in a distribution table
172  */
173 int stat_get_count_distrib_tbl(distrib_tbl_t *tbl) {
174         distrib_entry_t *entry;
175         counter_t cnt = ZERO_CNT;
176
177         foreach_pset(tbl->hash_map, entry)
178                 cnt_add(&cnt, &entry->cnt);
179         return cnt_to_uint(&cnt);
180 }
181
182 /*
183  * calculates the mean value of a distribution
184  */
185 double stat_calc_mean_distrib_tbl(distrib_tbl_t *tbl) {
186         distrib_entry_t *entry;
187         unsigned count;
188         double sum;
189
190         if (tbl->int_dist) {
191                 /* integer distribution, need min, max */
192                 int min, max;
193
194                 entry = pset_first(tbl->hash_map);
195
196                 if (! entry)
197                         return 0.0;
198
199                 min =
200                 max = PTR_TO_INT(entry->object);
201                 sum = cnt_to_dbl(&entry->cnt);
202
203
204                 for (entry = pset_next(tbl->hash_map); entry; entry = pset_next(tbl->hash_map)) {
205                         int value = PTR_TO_INT(entry->object);
206
207                         if (value < min)
208                                 min = value;
209                         if (value > max)
210                                 max = value;
211
212                         sum += cnt_to_dbl(&entry->cnt);
213                 }
214                 count = max - min + 1;
215         } else {
216                 sum = 0.0;
217                 count = 0;
218                 foreach_pset(tbl->hash_map, entry) {
219                         sum += cnt_to_dbl(&entry->cnt);
220                         ++count;
221                 }
222         }
223
224         return count ? sum / (double)count : 0.0;
225 }
226
227 /*
228  * calculates the average value of a distribution
229  */
230 double stat_calc_avg_distrib_tbl(distrib_tbl_t *tbl) {
231         distrib_entry_t *entry;
232         unsigned        count = 0;
233         double          sum   = 0.0;
234
235         if (tbl->int_dist) {
236                 if (pset_count(tbl->hash_map) <= 0)
237                         return 0.0;
238
239                 foreach_pset(tbl->hash_map, entry) {
240                         sum   += cnt_to_dbl(&entry->cnt) * PTR_TO_INT(entry->object);
241                         count += cnt_to_uint(&entry->cnt);
242                 }
243         } else {
244                 foreach_pset(tbl->hash_map, entry) {
245                         sum += cnt_to_dbl(&entry->cnt);
246                         ++count;
247                 }
248         }
249
250         return count ? sum / (double)count : 0.0;
251 }
252
253 /**
254  * iterates over all entries in a distribution table
255  */
256 void stat_iterate_distrib_tbl(const distrib_tbl_t *tbl, eval_distrib_entry_fun eval, void *env) {
257         distrib_entry_t *entry;
258
259         foreach_pset(tbl->hash_map, entry)
260                 eval(entry, env);
261 }