1 /* -----------------------------------------------------------------------------
2 * $Id: RetainerSet.c,v 1.1 2001/11/22 14:25:12 simonmar Exp $
4 * (c) The GHC Team, 2001
7 * Retainer set implementation for retainer profiling (see RetainerProfile.c)
9 * ---------------------------------------------------------------------------*/
16 #include "RetainerSet.h"
18 #include "Profiling.h"
22 #define HASH_TABLE_SIZE 255
23 #define hash(hk) (hk % HASH_TABLE_SIZE)
24 static RetainerSet *hashTable[HASH_TABLE_SIZE];
26 static Arena *arena; // arena in which we store retainer sets
28 static int nextId; // id of next retainer set
30 /* -----------------------------------------------------------------------------
31 * rs_MANY is a distinguished retainer set, such that
33 * isMember(e, rs_MANY) = True
35 * addElement(e, rs) = rs_MANY, if rs->num >= maxRetainerSetSize
36 * addElement(e, rs_MANY) = rs_MANY
38 * The point of rs_MANY is to keep the total number of retainer sets
39 * from growing too large.
40 * -------------------------------------------------------------------------- */
41 RetainerSet rs_MANY = {
50 nat maxRetainerSetSize = 16;
52 /* -----------------------------------------------------------------------------
53 * calculate the size of a RetainerSet structure
54 * -------------------------------------------------------------------------- */
56 sizeofRetainerSet( int elems )
58 return (sizeof(RetainerSet) + elems * sizeof(retainer));
61 /* -----------------------------------------------------------------------------
62 * Creates the first pool and initializes hashTable[].
63 * Frees all pools if any.
64 * -------------------------------------------------------------------------- */
66 initializeAllRetainerSet(void)
72 for (i = 0; i < HASH_TABLE_SIZE; i++)
74 nextId = 2; // Initial value must be positive, 2 is MANY.
77 /* -----------------------------------------------------------------------------
78 * Refreshes all pools for reuse and initializes hashTable[].
79 * -------------------------------------------------------------------------- */
81 refreshAllRetainerSet(void)
85 // Choose one of the following two approaches.
88 // first approach: completely refresh
92 for (i = 0; i < HASH_TABLE_SIZE; i++)
95 #endif // FIRST_APPROACH
97 #ifdef SECOND_APPROACH
98 // second approach: leave all the retainer sets for reuse
100 for (i = 0;i < HASH_TABLE_SIZE; i++) {
108 #endif // SECOND_APPROACH
111 /* -----------------------------------------------------------------------------
113 * -------------------------------------------------------------------------- */
115 closeAllRetainerSet(void)
120 /* -----------------------------------------------------------------------------
121 * Finds or creates if needed a singleton retainer set.
122 * -------------------------------------------------------------------------- */
124 singleton(retainer r)
129 hk = hashKeySingleton(r);
130 for (rs = hashTable[hash(hk)]; rs != NULL; rs = rs->link)
131 if (rs->num == 1 && rs->element[0] == r) return rs; // found it
134 rs = arenaAlloc( arena, sizeofRetainerSet(1) );
138 rs->link = hashTable[hash(hk)];
142 // The new retainer set is placed at the head of the linked list.
143 hashTable[hash(hk)] = rs;
148 /* -----------------------------------------------------------------------------
149 * Finds or creates a retainer set *rs augmented with r.
151 * r is not a member of rs, i.e., isMember(r, rs) returns rtsFalse.
154 * We could check if rs is NULL, in which case this function call
155 * reverts to singleton(). We do not choose this strategy because
156 * in most cases addElement() is invoked with non-NULL rs.
157 * -------------------------------------------------------------------------- */
159 addElement(retainer r, RetainerSet *rs)
162 nat nl; // Number of retainers in *rs Less than r
163 RetainerSet *nrs; // New Retainer Set
164 StgWord hk; // Hash Key
166 #ifdef DEBUG_RETAINER
167 // fprintf(stderr, "addElement(%p, %p) = ", r, rs);
171 ASSERT(rs->num <= maxRetainerSetSize);
173 if (rs == &rs_MANY || rs->num == maxRetainerSetSize) {
177 ASSERT(!isMember(r, rs));
179 for (nl = 0; nl < rs->num; nl++)
180 if (r < rs->element[nl]) break;
181 // Now nl is the index for r into the new set.
182 // Also it denotes the number of retainers less than r in *rs.
183 // Thus, compare the first nl retainers, then r itself, and finally the
184 // remaining (rs->num - nl) retainers.
186 hk = hashKeyAddElement(r, rs);
187 for (nrs = hashTable[hash(hk)]; nrs != NULL; nrs = nrs->link) {
188 // test *rs and *nrs for equality
191 if (rs->num + 1 != nrs->num) continue;
193 // compare the first nl retainers and find the first non-matching one.
194 for (i = 0; i < nl; i++)
195 if (rs->element[i] != nrs->element[i]) break;
196 if (i < nl) continue;
199 if (r != nrs->element[i]) continue; // i == nl
201 // compare the remaining retainers
202 for (; i < rs->num; i++)
203 if (rs->element[i] != nrs->element[i + 1]) break;
204 if (i < rs->num) continue;
206 #ifdef DEBUG_RETAINER
207 // fprintf(stderr, "%p\n", nrs);
209 // The set we are seeking already exists!
213 // create a new retainer set
214 nrs = arenaAlloc( arena, sizeofRetainerSet(rs->num + 1) );
215 nrs->num = rs->num + 1;
218 nrs->link = hashTable[hash(hk)];
220 for (i = 0; i < nl; i++) { // copy the first nl retainers
221 nrs->element[i] = rs->element[i];
223 nrs->element[i] = r; // copy r
224 for (; i < rs->num; i++) { // copy the remaining retainers
225 nrs->element[i + 1] = rs->element[i];
228 hashTable[hash(hk)] = nrs;
230 #ifdef DEBUG_RETAINER
231 // fprintf(stderr, "%p\n", nrs);
236 /* -----------------------------------------------------------------------------
237 * Call f() for each retainer set.
238 * -------------------------------------------------------------------------- */
240 traverseAllRetainerSet(void (*f)(RetainerSet *))
246 for (i = 0; i < HASH_TABLE_SIZE; i++)
247 for (rs = hashTable[i]; rs != NULL; rs = rs->link)
252 /* -----------------------------------------------------------------------------
253 * printRetainer() prints the full information on a given retainer,
254 * not a retainer set.
255 * -------------------------------------------------------------------------- */
256 #if defined(RETAINER_SCHEME_INFO)
257 // Retainer scheme 1: retainer = info table
259 printRetainer(FILE *f, retainer itbl)
261 fprintf(f, "%s[%s]", itbl->prof.closure_desc, itbl->prof.closure_type);
263 #elif defined(RETAINER_SCHEME_CCS)
264 // Retainer scheme 2: retainer = cost centre stack
266 printRetainer(FILE *f, retainer ccs)
270 #elif defined(RETAINER_SCHEME_CC)
271 // Retainer scheme 3: retainer = cost centre
273 printRetainer(FILE *f, retainer cc)
275 fprintf(f,"%s.%s", cc->module, cc->label);
279 /* -----------------------------------------------------------------------------
280 * printRetainerSetShort() should always display the same output for
281 * a given retainer set regardless of the time of invocation.
282 * -------------------------------------------------------------------------- */
283 #ifdef SECOND_APPROACH
284 #if defined(RETAINER_SCHEME_INFO)
285 // Retainer scheme 1: retainer = info table
287 printRetainerSetShort(FILE *f, RetainerSet *rs)
289 #define MAX_RETAINER_SET_SPACE 24
290 char tmp[MAX_RETAINER_SET_SPACE + 1];
296 tmp[MAX_RETAINER_SET_SPACE] = '\0';
298 // No blank characters are allowed.
299 sprintf(tmp + 0, "(%d)", -(rs->id));
301 ASSERT(size < MAX_RETAINER_SET_SPACE);
303 for (j = 0; j < rs->num; j++) {
304 if (j < rs->num - 1) {
305 strncpy(tmp + size, rs->element[j]->prof.closure_desc, MAX_RETAINER_SET_SPACE - size);
307 if (size == MAX_RETAINER_SET_SPACE)
309 strncpy(tmp + size, ",", MAX_RETAINER_SET_SPACE - size);
311 if (size == MAX_RETAINER_SET_SPACE)
315 strncpy(tmp + size, rs->element[j]->prof.closure_desc, MAX_RETAINER_SET_SPACE - size);
316 // size = strlen(tmp);
321 #elif defined(RETAINER_SCHEME_CC)
322 // Retainer scheme 3: retainer = cost centre
324 printRetainerSetShort(FILE *f, RetainerSet *rs)
326 #define MAX_RETAINER_SET_SPACE 24
327 char tmp[MAX_RETAINER_SET_SPACE + 1];
332 #elif defined(RETAINER_SCHEME_CCS)
333 // Retainer scheme 2: retainer = cost centre stack
335 printRetainerSetShort(FILE *f, RetainerSet *rs)
337 #define MAX_RETAINER_SET_SPACE 24
338 char tmp[MAX_RETAINER_SET_SPACE + 1];
344 tmp[MAX_RETAINER_SET_SPACE] = '\0';
346 // No blank characters are allowed.
347 sprintf(tmp + 0, "(%d)", -(rs->id));
349 ASSERT(size < MAX_RETAINER_SET_SPACE);
351 for (j = 0; j < rs->num; j++) {
352 if (j < rs->num - 1) {
353 strncpy(tmp + size, rs->element[j]->cc->label, MAX_RETAINER_SET_SPACE - size);
355 if (size == MAX_RETAINER_SET_SPACE)
357 strncpy(tmp + size, ",", MAX_RETAINER_SET_SPACE - size);
359 if (size == MAX_RETAINER_SET_SPACE)
363 strncpy(tmp + size, rs->element[j]->cc->label, MAX_RETAINER_SET_SPACE - size);
364 // size = strlen(tmp);
369 #elif defined(RETAINER_SCHEME_CC)
370 // Retainer scheme 3: retainer = cost centre
372 printRetainerSetShort(FILE *f, retainerSet *rs)
374 #define MAX_RETAINER_SET_SPACE 24
375 char tmp[MAX_RETAINER_SET_SPACE + 1];
381 tmp[MAX_RETAINER_SET_SPACE] = '\0';
383 // No blank characters are allowed.
384 sprintf(tmp + 0, "(%d)", -(rs->id));
386 ASSERT(size < MAX_RETAINER_SET_SPACE);
388 for (j = 0; j < rs->num; j++) {
389 if (j < rs->num - 1) {
390 strncpy(tmp + size, rs->element[j]->label,
391 MAX_RETAINER_SET_SPACE - size);
393 if (size == MAX_RETAINER_SET_SPACE)
395 strncpy(tmp + size, ",", MAX_RETAINER_SET_SPACE - size);
397 if (size == MAX_RETAINER_SET_SPACE)
401 strncpy(tmp + size, rs->element[j]->label,
402 MAX_RETAINER_SET_SPACE - size);
403 // size = strlen(tmp);
408 #define MAX_RETAINER_SET_SPACE 24
410 // 1. 32 > MAX_RETAINER_SET_SPACE + 1 (1 for '\0')
411 // 2. (MAX_RETAINER_SET_SPACE - DOT_NUMBER ) characters should be enough for
412 // printing one natural number (plus '(' and ')').
419 // No blank characters are allowed.
420 sprintf(tmp + 0, "(%d)", -(rs->id));
422 ASSERT(size < MAX_RETAINER_SET_SPACE - DOT_NUMBER);
424 for (j = 0; j < rs->num; j++) {
425 ts = strlen(rs->element[j]->label);
426 if (j < rs->num - 1) {
427 if (size + ts + 1 > MAX_RETAINER_SET_SPACE - DOT_NUMBER) {
428 sprintf(tmp + size, "...");
431 sprintf(tmp + size, "%s,", rs->element[j]->label);
435 if (size + ts > MAX_RETAINER_SET_SPACE - DOT_NUMBER) {
436 sprintf(tmp + size, "...");
439 sprintf(tmp + size, "%s", rs->element[j]->label);
446 #endif /* RETAINER_SCHEME_CC */
447 #endif /* SECOND_APPROACH */
449 /* -----------------------------------------------------------------------------
450 * Print the statistics. This function is called after each
451 * retainer profiling. *allCost is set the sum of all costs retained
452 * by any retainer sets. *numSet is set to the number of all
453 * retainer sets (including those with 0 cost).
454 * -------------------------------------------------------------------------- */
456 outputRetainerSet( FILE *hp_file, nat *allCost, nat *numSet )
459 #ifdef FIRST_APPROACH
467 duration = mut_user_time_during_RP();
469 fprintf(hp_file, "MARK %f\n", duration);
470 fprintf(hp_file, "BEGIN_SAMPLE %f\n", duration);
472 if (rs_MANY.cost > 0) {
473 fprintf(hp_file, "MANY\t%u\n", rs_MANY.cost * sizeof(StgWord));
476 for (i = 0; i < HASH_TABLE_SIZE; i++) {
477 for (rs = hashTable[i]; rs != NULL; rs = rs->link) {
480 Note: If rs->cost is 0, it means that there exists at
481 least one object which is retained by this retainer set
482 *rs temporarily. Since its new retainer set of this
483 object (replacing *rs) is at least larger than *rs, if
484 the cost of every object was a positive quantity, the
485 following invariants would hold: If rs->cost == 0, there
486 exists a retainer set rs' such that rs'->cost > 0 and
487 rs'->num > rs->num. However, static objects cost zero,
488 this does not hold. If we set the cost of each static
489 object to a positive quantity, it should hold, which is
495 *allCost += rs->cost;
497 #ifdef SECOND_APPROACH
498 if (rs->id > 0) // if having a positive cost for the first time?
499 rs->id = -(rs->id); // mark as having a positive cost
500 // Now, this retainer set has a permanent negative id.
502 // report in the unit of bytes: * sizeof(StgWord)
503 printRetainerSetShort(hp_file, rs);
504 fprintf(hp_file, "\t%u\n", rs->cost * sizeof(StgWord));
507 #ifdef FIRST_APPROACH
508 fprintf(hp_file, "{");
509 for (j = 0; j < rs->num - 1; j++) {
510 printRetainer(hp_file, rs->element[j]);
511 fprintf(hp_file, ",");
513 printRetainer(hp_file, rs->element[j]);
514 fprintf(hp_file, "}\t%u\n", rs->cost * sizeof(StgWord));
518 fprintf(hp_file, "END_SAMPLE %f\n", duration);
522 This function is called at the exit of the program.
524 #ifdef SECOND_APPROACH
526 outputAllRetainerSet(FILE *prof_file)
530 RetainerSet *rs, **rsArray, *tmp;
532 // find out the number of retainer sets which have had a non-zero cost at
533 // least once during retainer profiling
535 for (i = 0; i < HASH_TABLE_SIZE; i++)
536 for (rs = hashTable[i]; rs != NULL; rs = rs->link) {
541 if (numSet == 0) // retainer profiling was not done at all.
545 rsArray = stgMallocBytes(numSet * sizeof(RetainerSet *),
546 "outputAllRetainerSet()");
548 // prepare for sorting
550 for (i = 0; i < HASH_TABLE_SIZE; i++)
551 for (rs = hashTable[i]; rs != NULL; rs = rs->link) {
560 // sort rsArray[] according to the id of each retainer set
561 for (i = numSet - 1; i > 0; i--) {
562 for (j = 0; j <= i - 1; j++) {
563 // if (-(rsArray[j]->id) < -(rsArray[j + 1]->id))
564 if (rsArray[j]->id < rsArray[j + 1]->id) {
566 rsArray[j] = rsArray[j + 1];
567 rsArray[j + 1] = tmp;
572 fprintf(prof_file, "\nRetainer sets created during profiling:\n");
573 for (i = 0;i < numSet; i++) {
574 fprintf(prof_file, "SET %u = {", -(rsArray[i]->id));
575 for (j = 0; j < rsArray[i]->num - 1; j++) {
576 printRetainer(prof_file, rsArray[i]->element[j]);
577 fprintf(prof_file, ", ");
579 printRetainer(prof_file, rsArray[i]->element[j]);
580 fprintf(prof_file, "}\n");
585 #endif // SECOND_APPROACH
587 #endif /* PROFILING */