Use foreach_pset().
[libfirm] / ir / stat / distrib.c
1 /*
2  * Copyright (C) 1995-2011 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  */
25 #include "config.h"
26
27 #include "hashptr.h"
28 #include "util.h"
29 #include "xmalloc.h"
30 #include "firmstat_t.h"
31
32 /**
33  * calculates a hash value for an address
34  */
35 static unsigned addr_hash(const void *object)
36 {
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 {
45         return (unsigned)PTR_TO_INT(object);
46 }
47
48 /**
49  * compare function for integer distribution tables
50  */
51 static int int_cmp_fun(const void *elt, const void *key)
52 {
53         const distrib_entry_t *p1 = (const distrib_entry_t*)elt;
54         const distrib_entry_t *p2 = (const distrib_entry_t*)key;
55
56         if (p1->object == p2->object)
57                 return 0;
58         return p1->object < p2->object ? -1 : 1;
59 }
60
61 /*
62  * create a new distribution table
63  */
64 distrib_tbl_t *stat_new_distrib_tbl(pset_cmp_fun cmp_func, distrib_hash_fun hash_func)
65 {
66         distrib_tbl_t *res = XMALLOC(distrib_tbl_t);
67
68         obstack_init(&res->cnts);
69
70         /* create the hash-table */
71         res->hash_map  = new_pset(cmp_func, 8);
72         res->hash_func = hash_func ? hash_func : addr_hash;
73         res->int_dist  = 0;
74
75         return res;
76 }
77
78 /*
79  * create a new distribution table for an integer distribution
80  */
81 distrib_tbl_t *stat_new_int_distrib_tbl(void)
82 {
83         distrib_tbl_t *res = stat_new_distrib_tbl(int_cmp_fun, int_hash);
84
85         if (res)
86                 res->int_dist = 1;
87
88         return res;
89 }
90
91 /*
92  * destroy a distribution table
93  */
94 void stat_delete_distrib_tbl(distrib_tbl_t *tbl)
95 {
96         if (tbl) {
97                 /* free all entries */
98                 obstack_free(&tbl->cnts, NULL);
99
100                 /* delete the hash table */
101                 del_pset(tbl->hash_map);
102         }
103 }
104
105 /**
106  * Returns the associates distrib_entry_t for an object
107  */
108 static distrib_entry_t *distrib_get_entry(distrib_tbl_t *tbl, const void *object)
109 {
110         distrib_entry_t key;
111         distrib_entry_t *elem;
112
113         key.object = object;
114
115         elem = (distrib_entry_t*)pset_find(tbl->hash_map, &key, tbl->hash_func(object));
116         if (elem)
117                 return elem;
118
119         elem = OALLOC(&tbl->cnts, distrib_entry_t);
120
121         /* clear counter */
122         cnt_clr(&elem->cnt);
123
124         elem->object = object;
125
126         return (distrib_entry_t*)pset_insert(tbl->hash_map, elem, tbl->hash_func(object));
127 }
128
129 /*
130  * adds a new object count into the distribution table
131  */
132 void stat_add_distrib_tbl(distrib_tbl_t *tbl, const void *object, const counter_t *cnt)
133 {
134         distrib_entry_t *elem = distrib_get_entry(tbl, object);
135
136         cnt_add(&elem->cnt, cnt);
137 }
138
139 /*
140  * adds a new key count into the integer distribution table
141  */
142 void stat_add_int_distrib_tbl(distrib_tbl_t *tbl, int key, const counter_t *cnt)
143 {
144         stat_add_distrib_tbl(tbl, INT_TO_PTR(key), cnt);
145 }
146
147 /*
148  * increases object count by one
149  */
150 void stat_inc_distrib_tbl(distrib_tbl_t *tbl, const void *object)
151 {
152         distrib_entry_t *elem = distrib_get_entry(tbl, object);
153
154         cnt_inc(&elem->cnt);
155 }
156
157 /*
158  * increases key count by one
159  */
160 void stat_inc_int_distrib_tbl(distrib_tbl_t *tbl, int key)
161 {
162         stat_inc_distrib_tbl(tbl, INT_TO_PTR(key));
163 }
164
165 /*
166  * inserts a new object with count 0 into the distribution table
167  * if object is already present, nothing happens
168  */
169 void stat_insert_distrib_tbl(distrib_tbl_t *tbl, const void *object)
170 {
171         /* executed for side effect */
172         (void)distrib_get_entry(tbl, object);
173 }
174
175 /*
176  * inserts a new key with count 0 into the integer distribution table
177  * if key is already present, nothing happens
178  */
179 void stat_insert_int_distrib_tbl(distrib_tbl_t *tbl, int key)
180 {
181         stat_insert_distrib_tbl(tbl, INT_TO_PTR(key));
182 }
183
184 /*
185  * returns the sum over all counters in a distribution table
186  */
187 int stat_get_count_distrib_tbl(distrib_tbl_t *tbl)
188 {
189         distrib_entry_t *entry;
190         counter_t cnt = ZERO_CNT;
191
192         foreach_pset(tbl->hash_map, distrib_entry_t*, entry)
193                 cnt_add(&cnt, &entry->cnt);
194         return cnt_to_uint(&cnt);
195 }
196
197 /*
198  * calculates the mean value of a distribution
199  */
200 double stat_calc_mean_distrib_tbl(distrib_tbl_t *tbl)
201 {
202         distrib_entry_t *entry;
203         size_t count;
204         double sum;
205
206         if (tbl->int_dist) {
207                 /* integer distribution, need min, max */
208                 if (pset_count(tbl->hash_map) == 0)
209                         return 0.0;
210
211                 int min = INT_MAX;
212                 int max = INT_MIN;
213                 sum = 0.0;
214
215                 foreach_pset(tbl->hash_map, distrib_entry_t*, entry) {
216                         int value = PTR_TO_INT(entry->object);
217
218                         if (value < min)
219                                 min = value;
220                         if (value > max)
221                                 max = value;
222
223                         sum += cnt_to_dbl(&entry->cnt);
224                 }
225                 count = max - min + 1;
226         } else {
227                 sum = 0.0;
228                 count = 0;
229                 foreach_pset(tbl->hash_map, distrib_entry_t*, entry) {
230                         sum += cnt_to_dbl(&entry->cnt);
231                         ++count;
232                 }
233         }
234
235         return count ? sum / (double)count : 0.0;
236 }
237
238 /*
239  * calculates the average value of a distribution
240  */
241 double stat_calc_avg_distrib_tbl(distrib_tbl_t *tbl)
242 {
243         distrib_entry_t *entry;
244         size_t          count = 0;
245         double          sum   = 0.0;
246
247         if (tbl->int_dist) {
248                 if (pset_count(tbl->hash_map) <= 0)
249                         return 0.0;
250
251                 foreach_pset(tbl->hash_map, distrib_entry_t*, entry) {
252                         sum   += cnt_to_dbl(&entry->cnt) * PTR_TO_INT(entry->object);
253                         count += cnt_to_uint(&entry->cnt);
254                 }
255         } else {
256                 foreach_pset(tbl->hash_map, distrib_entry_t*, entry) {
257                         sum += cnt_to_dbl(&entry->cnt);
258                         ++count;
259                 }
260         }
261
262         return count ? sum / (double)count : 0.0;
263 }
264
265 /**
266  * iterates over all entries in a distribution table
267  */
268 void stat_iterate_distrib_tbl(const distrib_tbl_t *tbl, eval_distrib_entry_fun eval, void *env)
269 {
270         distrib_entry_t *entry;
271
272         foreach_pset(tbl->hash_map, distrib_entry_t*, entry)
273                 eval(entry, env);
274 }