2 * Copyright (c) 1992 Barton P. Miller, Morgan Clark, Timothy Torzewski,
3 * Jeff Hollingsworth, and Bruce Irvin. All rights reserved.
5 * This software is furnished under the condition that it may not
6 * be provided or otherwise made available to, or used by, any
7 * other person. No title to or ownership of the software is
8 * hereby transferred. The name of the principals
9 * may not be used in any advertising or publicity related to this
10 * software without specific, written prior authorization.
11 * Any use of this software must include the above copyright notice.
16 * hist.C - routines to manage hisograms.
19 * Revision 1.4 1994/02/10 23:08:26 hollings
20 * Fixed list.h ++ function to work when a hash table has an element at
21 * slot zero in the table.
23 * Removed unused fields in hist class.
25 * Revision 1.3 1994/02/08 00:30:39 hollings
26 * Make libutil more compatable with ATT CC.
28 * Revision 1.2 1994/01/26 04:53:42 hollings
29 * Change to using <module>/h/*.h
31 * Revision 1.1 1994/01/25 20:50:25 hollings
32 * First real version of utility library.
34 * Revision 1.4 1993/12/15 21:06:23 hollings
35 * changed initial bucket width to 0.1.
37 * Revision 1.3 1993/08/11 18:03:54 hollings
38 * removed printf for folding hist.
40 * Revision 1.2 1993/08/05 18:55:08 hollings
41 * general fixups and include format.
43 * Revision 1.1 1993/05/07 20:19:17 hollings
54 #include "util/h/hist.h"
56 /* number of intervals at which we switch to regular histograms */
57 #define MAX_INTERVALS 15
59 static void smoothBins(Bin *bins, int i, timeStamp bucketSize);
61 int Histogram::numBins = 1000;
62 timeStamp Histogram::bucketSize = 0.1;
63 timeStamp Histogram::total_time = (Histogram::numBins * Histogram::bucketSize);
64 Histogram *Histogram::allHist = NULL;
65 int Histogram::lastGlobalBin = 0;
67 Histogram::Histogram(metricStyle type)
73 intervalLimit = MAX_INTERVALS;
74 storageType = HistInterval;
75 dataPtr.intervals = (Interval *) calloc(sizeof(Interval)*intervalLimit, 1);
81 * Given an array of buckets - turn them into a histogram.
84 Histogram::Histogram(Bin *buckets, metricStyle type)
86 // First call default constructor.
87 (void) Histogram(type);
89 storageType = HistBucket;
90 dataPtr.buckets = (Bin *) calloc(sizeof(Bin), numBins);
91 memcpy(dataPtr.buckets, buckets, sizeof(Bin)*numBins);
94 #define MAX(a, b) ((a) > (b) ? (a):(b))
97 * addInterval - add a value to a histogram from start to end.
99 * start, and end are relative to the global start time (i.e. 0.0 is the
100 * left side of the first hist bin).
102 void Histogram::addInterval(timeStamp start,
108 List<HistogramSubscriber*> currS;
109 Interval *currentInterval;
112 while ((end >= total_time) || (start >= total_time)) {
113 // colapse histogram.
117 lastBin = (int) (end / bucketSize);
118 lastGlobalBin = lastBin;
119 for (h=allHist; h; h=h->next) {
120 if ((h->lastBin < lastGlobalBin)) {
121 lastGlobalBin = h->lastBin;
125 if (value == 0.0) return;
127 if (storageType == HistInterval) {
128 if (intervalCount == intervalLimit) {
130 if (intervalLimit * sizeof(Interval) <
131 numBins * sizeof(Bin)) {
132 dataPtr.intervals = (Interval *)
133 realloc(dataPtr.intervals,
134 sizeof(Interval)*intervalLimit);
137 /* convert to buckets */
139 bucketValue(start, end, value, smooth);
143 currentInterval = &(dataPtr.intervals[intervalCount]);
144 assert(start >= 0.0);
145 assert(start <= total_time);
146 currentInterval->start = start;
149 assert(end <= total_time);
150 currentInterval->end = end;
152 currentInterval->value = value;
156 bucketValue(start, end, value, smooth);
158 for (currS = subscribers; *currS; currS++) {
159 (*currS)->callBack(HistNewValue, end, (*currS)->userData);
163 void Histogram::convertToBins()
167 Interval *currentInterval;
169 if (storageType == HistBucket) return;
171 intervals = dataPtr.intervals;
172 dataPtr.buckets = (Bin *) calloc(sizeof(Bin), numBins);
173 memset(dataPtr.buckets, '\0', sizeof(Bin)*numBins);
174 for (i=0; i < intervalCount; i++) {
175 currentInterval = &(intervals[i]);
176 bucketValue(currentInterval->start, currentInterval->end,
177 currentInterval->value, smooth);
182 storageType = HistBucket;
185 void Histogram::foldAllHist()
190 List<HistogramSubscriber*> currS;
194 total_time = (numBins * bucketSize);
197 for (curr=allHist; curr; curr= curr->next) {
198 // fold individual hist.
199 if (curr->storageType == HistBucket) {
200 bins = curr->dataPtr.buckets;
201 for (i=0; i < numBins/2; i++) {
202 bins[i] = bins[i*2] + bins[i*2+1];
205 memset(&bins[i], '\0', (numBins - i) * sizeof(Bin));
207 for (currS = curr->subscribers; *currS; currS++) {
208 (*currS)->callBack(HistNewTimeBase, bucketSize, (*currS)->userData);
213 int Histogram::subscribe(timeStamp maxRate,
214 subscriberCallBack func,
217 HistogramSubscriber *curr;
219 curr = new HistogramSubscriber(maxRate, func, userData);
220 subscribers.add(curr);
224 void Histogram::bucketValue(timeStamp start_clock,
230 int first_bin, last_bin;
231 timeStamp elapsed_clock = (timeStamp) 0.0;
232 timeStamp first_bin_start, last_bin_start;
233 sampleValue amt_first_bin, amt_last_bin, amt_other_bins;
234 timeStamp time_in_first_bin, time_in_last_bin, time_in_other_bins;
237 elapsed_clock = end_clock - start_clock;
239 /* set starting and ending bins */
240 first_bin = (int) (start_clock / bucketSize);
241 assert(first_bin >= 0);
242 assert(first_bin <= numBins);
243 if (first_bin == numBins)
244 first_bin = numBins-1;
245 last_bin = (int) (end_clock / bucketSize);
246 assert(last_bin >= 0);
247 assert(last_bin <= numBins);
248 if (last_bin == numBins)
249 last_bin = numBins-1;
250 /* set starting times for first & last bins */
251 first_bin_start = bucketSize * first_bin;
252 last_bin_start = bucketSize * last_bin;
254 if (metricType == SampledFunction) {
255 for (i=first_bin; i <= last_bin; i++) {
256 dataPtr.buckets[i] = value * bucketSize;
259 if (last_bin == first_bin) {
260 dataPtr.buckets[first_bin] += value;
261 if (smooth && (dataPtr.buckets[first_bin] > bucketSize)) {
263 smoothBins(dataPtr.buckets, first_bin, bucketSize);
268 /* determine how much of the first & last bins were in this interval */
269 time_in_first_bin = bucketSize - (start_clock - first_bin_start);
270 time_in_last_bin = end_clock - last_bin_start;
272 MAX(elapsed_clock - (time_in_first_bin + time_in_last_bin), 0);
274 /* determine how much of value should be in each bin in the interval */
275 amt_first_bin = (time_in_first_bin / elapsed_clock) * value;
276 amt_last_bin = (time_in_last_bin / elapsed_clock) * value;
277 amt_other_bins = (time_in_other_bins / elapsed_clock) * value;
278 if (last_bin > first_bin+1)
279 amt_other_bins /= (last_bin - first_bin) - 1.0;
281 /* add the appropriate amount of time to each bin */
282 dataPtr.buckets[first_bin] += amt_first_bin;
283 dataPtr.buckets[last_bin] += amt_last_bin;
284 for (i=first_bin+1; i < last_bin; i++)
285 dataPtr.buckets[i] += amt_other_bins;
288 /* limit absurd time values - anything over 100% is pushed back into
291 for (i = first_bin; i <= last_bin; i++) {
292 if (dataPtr.buckets[i] > bucketSize)
293 smoothBins(dataPtr.buckets, i, bucketSize);
299 static void smoothBins(Bin *bins, int i, timeStamp bucketSize)
302 sampleValue extra_time;
304 extra_time = (bins[i] - bucketSize);
305 bins[i] = bucketSize;
307 while ((j >= 0) && (extra_time > 0.0)) {
308 bins[j] += extra_time;
310 if (bins[j] > bucketSize) {
311 extra_time = (bins[j] - bucketSize);
312 bins[j] = bucketSize;
316 if (extra_time > 0.0) {
317 fprintf(stderr, "**** Unable to bucket %f seconds\n", extra_time);
323 static double splitTime(timeStamp startInterval,
324 timeStamp endInterval,
325 timeStamp startLimit,
330 timeStamp inv_length;
332 inv_length = endInterval-startInterval;
333 if ((startInterval >= startLimit) && (endInterval <= endLimit)) {
335 } else if (startInterval == endInterval) {
336 /* can't split delta functions */
338 } else if ((startInterval >= startLimit) && (startInterval <= endLimit)) {
339 /* overlaps the end */
340 time = value * (endLimit - startInterval) / inv_length;
341 } else if ((endInterval <= endLimit) && (endInterval >= startLimit)) {
342 /* overlaps the end */
343 time = value * (endInterval - startLimit) / inv_length;
344 } else if ((startInterval < startLimit) && (endInterval > endLimit)) {
345 /* detail contains interval */
346 time = value * (endLimit - startLimit) / inv_length;
348 /* no overlap of time */
355 * Get the component of the histogram from start to end.
358 sampleValue Histogram::getValue(timeStamp start, timeStamp end)
362 sampleValue retVal = 0.0;
363 int first_bin, last_bin;
364 double pct_first_bin, pct_last_bin;
366 if (storageType == HistInterval) {
367 if (metricType == SampledFunction) {
368 // compute weight average of sample.
369 for (i=0; i < intervalCount; i++) {
370 cp = &(dataPtr.intervals[i]);
371 retVal += cp->value * (cp->end - cp->start);
374 // total over the interval.
375 for (i=0; i < intervalCount; i++) {
376 cp = &(dataPtr.intervals[i]);
377 retVal += splitTime(cp->start, cp->end, start, end, cp->value);
381 first_bin = (int) (start/bucketSize);
383 last_bin = (int) (end/bucketSize+0.5);
384 if (last_bin >= numBins) last_bin = numBins-1;
385 if (first_bin == last_bin) {
386 retVal = dataPtr.buckets[first_bin];
388 /* (first_bin+1)*bucketSize == time at end of first bucket */
389 pct_first_bin = (((first_bin+1)*bucketSize)-start)/bucketSize;
390 retVal += pct_first_bin * dataPtr.buckets[first_bin];
391 /* last_bin+*bucketSize == time at start of last bucket */
392 pct_last_bin = (end - last_bin*bucketSize)/bucketSize;
393 retVal += pct_last_bin * dataPtr.buckets[last_bin];
394 for (i=first_bin+1; i <= last_bin-1; i++) {
395 retVal += dataPtr.buckets[i];
402 sampleValue Histogram::getValue()
404 return(getValue(0.0, numBins * bucketSize));
407 HistogramSubscriber::HistogramSubscriber(timeStamp max,
408 subscriberCallBack func,