gem5 / public / gem5 / 80ae19088580df463907a1d4e23b92b856f652b0 / . / src / base / stats / storage.hh

/* | |

* Copyright (c) 2021 Daniel R. Carvalho | |

* Copyright (c) 2003-2005 The Regents of The University of Michigan | |

* All rights reserved. | |

* | |

* Redistribution and use in source and binary forms, with or without | |

* modification, are permitted provided that the following conditions are | |

* met: redistributions of source code must retain the above copyright | |

* notice, this list of conditions and the following disclaimer; | |

* redistributions in binary form must reproduce the above copyright | |

* notice, this list of conditions and the following disclaimer in the | |

* documentation and/or other materials provided with the distribution; | |

* neither the name of the copyright holders nor the names of its | |

* contributors may be used to endorse or promote products derived from | |

* this software without specific prior written permission. | |

* | |

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |

* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |

* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |

* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |

* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |

* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |

* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |

* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |

* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |

* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |

* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |

*/ | |

#ifndef __BASE_STATS_STORAGE_HH__ | |

#define __BASE_STATS_STORAGE_HH__ | |

#include <cassert> | |

#include <cmath> | |

#include "base/cast.hh" | |

#include "base/compiler.hh" | |

#include "base/logging.hh" | |

#include "base/stats/types.hh" | |

#include "sim/cur_tick.hh" | |

namespace gem5 | |

{ | |

GEM5_DEPRECATED_NAMESPACE(Stats, statistics); | |

namespace statistics | |

{ | |

struct StorageParams | |

{ | |

virtual ~StorageParams() = default; | |

}; | |

/** | |

* Templatized storage and interface for a simple scalar stat. | |

*/ | |

class StatStor | |

{ | |

private: | |

/** The statistic value. */ | |

Counter data; | |

public: | |

struct Params : public StorageParams {}; | |

/** | |

* Builds this storage element and calls the base constructor of the | |

* datatype. | |

*/ | |

StatStor(const StorageParams* const storage_params) | |

: data(Counter()) | |

{ } | |

/** | |

* The the stat to the given value. | |

* @param val The new value. | |

*/ | |

void set(Counter val) { data = val; } | |

/** | |

* Increment the stat by the given value. | |

* @param val The new value. | |

*/ | |

void inc(Counter val) { data += val; } | |

/** | |

* Decrement the stat by the given value. | |

* @param val The new value. | |

*/ | |

void dec(Counter val) { data -= val; } | |

/** | |

* Return the value of this stat as its base type. | |

* @return The value of this stat. | |

*/ | |

Counter value() const { return data; } | |

/** | |

* Return the value of this stat as a result type. | |

* @return The value of this stat. | |

*/ | |

Result result() const { return (Result)data; } | |

/** | |

* Prepare stat data for dumping or serialization | |

*/ | |

void prepare(const StorageParams* const storage_params) { } | |

/** | |

* Reset stat value to default | |

*/ | |

void reset(const StorageParams* const storage_params) { data = Counter(); } | |

/** | |

* @return true if zero value | |

*/ | |

bool zero() const { return data == Counter(); } | |

}; | |

/** | |

* Templatized storage and interface to a per-tick average stat. This keeps | |

* a current count and updates a total (count * ticks) when this count | |

* changes. This allows the quick calculation of a per tick count of the item | |

* being watched. This is good for keeping track of residencies in structures | |

* among other things. | |

*/ | |

class AvgStor | |

{ | |

private: | |

/** The current count. */ | |

Counter current; | |

/** The tick of the last reset */ | |

Tick lastReset; | |

/** The total count for all tick. */ | |

mutable Result total; | |

/** The tick that current last changed. */ | |

mutable Tick last; | |

public: | |

struct Params : public StorageParams {}; | |

/** | |

* Build and initializes this stat storage. | |

*/ | |

AvgStor(const StorageParams* const storage_params) | |

: current(0), lastReset(0), total(0), last(0) | |

{ } | |

/** | |

* Set the current count to the one provided, update the total and last | |

* set values. | |

* @param val The new count. | |

*/ | |

void | |

set(Counter val) | |

{ | |

total += current * (curTick() - last); | |

last = curTick(); | |

current = val; | |

} | |

/** | |

* Increment the current count by the provided value, calls set. | |

* @param val The amount to increment. | |

*/ | |

void inc(Counter val) { set(current + val); } | |

/** | |

* Deccrement the current count by the provided value, calls set. | |

* @param val The amount to decrement. | |

*/ | |

void dec(Counter val) { set(current - val); } | |

/** | |

* Return the current count. | |

* @return The current count. | |

*/ | |

Counter value() const { return current; } | |

/** | |

* Return the current average. | |

* @return The current average. | |

*/ | |

Result | |

result() const | |

{ | |

assert(last == curTick()); | |

return (Result)(total + current) / (Result)(curTick() - lastReset + 1); | |

} | |

/** | |

* @return true if zero value | |

*/ | |

bool zero() const { return total == 0.0; } | |

/** | |

* Prepare stat data for dumping or serialization | |

*/ | |

void | |

prepare(const StorageParams* const storage_params) | |

{ | |

total += current * (curTick() - last); | |

last = curTick(); | |

} | |

/** | |

* Reset stat value to default | |

*/ | |

void | |

reset(const StorageParams* const storage_params) | |

{ | |

total = 0.0; | |

last = curTick(); | |

lastReset = curTick(); | |

} | |

}; | |

/** The parameters for a distribution stat. */ | |

struct DistParams : public StorageParams | |

{ | |

const DistType type; | |

DistParams(DistType t) : type(t) {} | |

}; | |

/** | |

* Templatized storage and interface for a distribution stat. A distribution | |

* uses buckets to keep track of values within a given range. All other | |

* values, although accounted for on the overall calculations, are not tracked | |

* in buckets themselves; two special counters, underflow and overflow store | |

* the number of occurrences of such values. | |

*/ | |

class DistStor | |

{ | |

private: | |

/** The minimum value to track. */ | |

Counter min_track; | |

/** The maximum value to track. */ | |

Counter max_track; | |

/** The number of entries in each bucket. */ | |

Counter bucket_size; | |

/** The smallest value sampled. */ | |

Counter min_val; | |

/** The largest value sampled. */ | |

Counter max_val; | |

/** The number of values sampled less than min. */ | |

Counter underflow; | |

/** The number of values sampled more than max. */ | |

Counter overflow; | |

/** The current sum. */ | |

Counter sum; | |

/** The sum of squares. */ | |

Counter squares; | |

/** The number of samples. */ | |

Counter samples; | |

/** Counter for each bucket. */ | |

VCounter cvec; | |

public: | |

/** The parameters for a distribution stat. */ | |

struct Params : public DistParams | |

{ | |

/** The minimum value to track. */ | |

Counter min; | |

/** The maximum value to track. */ | |

Counter max; | |

/** The number of entries in each bucket. */ | |

Counter bucket_size; | |

/** The number of buckets. Equal to (max-min)/bucket_size. */ | |

size_type buckets; | |

Params(Counter _min, Counter _max, Counter _bucket_size) | |

: DistParams(Dist), min(_min), max(_max), bucket_size(_bucket_size), | |

buckets(0) | |

{ | |

fatal_if(bucket_size <= 0, | |

"Bucket size (%f) must be greater than zero", bucket_size); | |

warn_if(std::floor((max - min + 1.0) / bucket_size) != | |

std::ceil((max - min + 1.0) / bucket_size), | |

"Bucket size (%f) does not divide range [%f:%f] into equal-" \ | |

"sized buckets. Rounding up.", bucket_size, min + 1.0, max); | |

buckets = std::ceil((max - min + 1.0) / bucket_size); | |

} | |

}; | |

DistStor(const StorageParams* const storage_params) | |

: cvec(safe_cast<const Params *>(storage_params)->buckets) | |

{ | |

reset(storage_params); | |

} | |

/** | |

* Add a value to the distribution for the given number of times. | |

* @param val The value to add. | |

* @param number The number of times to add the value. | |

*/ | |

void sample(Counter val, int number); | |

/** | |

* Return the number of buckets in this distribution. | |

* @return the number of buckets. | |

*/ | |

size_type size() const { return cvec.size(); } | |

/** | |

* Returns true if any calls to sample have been made. | |

* @return True if any values have been sampled. | |

*/ | |

bool | |

zero() const | |

{ | |

return samples == Counter(); | |

} | |

void | |

prepare(const StorageParams* const storage_params, DistData &data) | |

{ | |

const Params *params = safe_cast<const Params *>(storage_params); | |

assert(params->type == Dist); | |

data.type = params->type; | |

data.min = params->min; | |

data.max = params->max; | |

data.bucket_size = params->bucket_size; | |

data.min_val = (min_val == CounterLimits::max()) ? 0 : min_val; | |

data.max_val = (max_val == CounterLimits::min()) ? 0 : max_val; | |

data.underflow = underflow; | |

data.overflow = overflow; | |

data.cvec.resize(params->buckets); | |

for (off_type i = 0; i < params->buckets; ++i) | |

data.cvec[i] = cvec[i]; | |

data.sum = sum; | |

data.squares = squares; | |

data.samples = samples; | |

} | |

/** | |

* Reset stat value to default | |

*/ | |

void | |

reset(const StorageParams* const storage_params) | |

{ | |

const Params *params = safe_cast<const Params *>(storage_params); | |

min_track = params->min; | |

max_track = params->max; | |

bucket_size = params->bucket_size; | |

min_val = CounterLimits::max(); | |

max_val = CounterLimits::min(); | |

underflow = Counter(); | |

overflow = Counter(); | |

size_type size = cvec.size(); | |

for (off_type i = 0; i < size; ++i) | |

cvec[i] = Counter(); | |

sum = Counter(); | |

squares = Counter(); | |

samples = Counter(); | |

} | |

}; | |

/** | |

* Templatized storage and interface for a histogram stat. | |

* | |

* The number of buckets is fixed on initialization; however, the bucket size | |

* isn't. That means that when samples that are outside the current range are | |

* seen, the bucket size will be increased so that each bucket can hold a | |

* bigger range of values. When that happens, the bucket's contents are re- | |

* located. | |

* | |

* The min and max bucket values can only be, respectively, decreased and | |

* increased when sampling. If this wasn't true, samples that were previously | |

* within the buclet range could not be anymore within the valid range, making | |

* the storage's state incoherent. These values are set back to their initial | |

* states on reset(). | |

* | |

* The bucket range always is zero-centric. While the storage does not | |

* contain negative values the bucket range will keep its lower bound at | |

* zero, doubling the upper bound when needed; However, as soon a negative | |

* value is sampled, zero becomes the lower bound of the middle (rounded up) | |

* bucket. Although this means that the histogram will not be symmetric if | |

* negative values are sampled, it makes it possible to grow the buckets | |

* without keeping track of the individual values. | |

* | |

* This happens because if zero was not a lower or upper bound, when its | |

* value was doubled, the lower and upper bound of the bucket containing | |

* zero would intersect with middle values of the previous and next buckets. | |

* For example, if the bucket containing zero has range [-2,2[, therefore | |

* its neighbor buckets would have ranges at [-6,-2[ and [2,6[. When the | |

* buckets are grown, the zero bucket would grow its range to [-4,4[, which | |

* cannot be easily extracted from the neighor buckets. | |

*/ | |

class HistStor | |

{ | |

private: | |

/** Lower bound of the first bucket's range. */ | |

Counter min_bucket; | |

/** Lower bound of the last bucket's range. */ | |

Counter max_bucket; | |

/** The number of entries in each bucket. */ | |

Counter bucket_size; | |

/** The current sum. */ | |

Counter sum; | |

/** The sum of logarithm of each sample, used to compute geometric mean. */ | |

Counter logs; | |

/** The sum of squares. */ | |

Counter squares; | |

/** The number of samples. */ | |

Counter samples; | |

/** Counter for each bucket. */ | |

VCounter cvec; | |

/** | |

* Given a bucket size B, and a range of values [0, N], this function | |

* doubles the bucket size to double the range of values towards the | |

* positive infinite; that is, double the upper range of this storage | |

* so that the range becomes [0, 2*N]. | |

* | |

* Because the bucket size is doubled, the buckets contents are rearranged, | |

* since the original range of values is mapped to the lower half buckets. | |

*/ | |

void growUp(); | |

/** | |

* Given a bucket size B, and a range of values [M, N], where M < 0, this | |

* function doubles the bucket size to double the range of values towards | |

* both positive and negative infinites; that is, it doubles both the lower | |

* and the upper range of this storage so that the range becomes | |

* [2*M, 2*N]. | |

* | |

* Because the bucket size is doubled, the buckets contents are | |

* rearranged, and the original range of values are redistributed to free | |

* buckets for the newly appended ranges. | |

*/ | |

void growOut(); | |

/** | |

* Given a bucket size B, and a range of values [0, N], this function | |

* doubles the bucket size to double the range of values towards the | |

* negative infinite; that is, it doubles the lower range of this | |

* storage so that the middle buckes contaihs zero as a lower bound. As | |

* such, the storage range becomes [-N, N+B] if there is an odd number | |

* of buckets, and [-N-B, N+B] if there is an even number of buckets. | |

* | |

* Because the bucket size is doubled, the buckets contents are | |

* rearranged, and the original range of values are redistributed to free | |

* buckets for the newly appended ranges. | |

*/ | |

void growDown(); | |

public: | |

/** The parameters for a distribution stat. */ | |

struct Params : public DistParams | |

{ | |

/** The number of buckets. */ | |

size_type buckets; | |

Params(size_type _buckets) | |

: DistParams(Hist) | |

{ | |

fatal_if(_buckets < 2, | |

"There must be at least two buckets in a histogram"); | |

buckets = _buckets; | |

} | |

}; | |

HistStor(const StorageParams* const storage_params) | |

: cvec(safe_cast<const Params *>(storage_params)->buckets) | |

{ | |

reset(storage_params); | |

} | |

/** | |

* Adds the contents of the given storage to this storage. | |

* @param other The other storage to be added. | |

*/ | |

void add(HistStor *other); | |

/** | |

* Add a value to the distribution for the given number of times. | |

* @param val The value to add. | |

* @param number The number of times to add the value. | |

*/ | |

void sample(Counter val, int number); | |

/** | |

* Return the number of buckets in this distribution. | |

* @return the number of buckets. | |

*/ | |

size_type size() const { return cvec.size(); } | |

/** | |

* Returns true if any calls to sample have been made. | |

* @return True if any values have been sampled. | |

*/ | |

bool | |

zero() const | |

{ | |

return samples == Counter(); | |

} | |

void | |

prepare(const StorageParams* const storage_params, DistData &data) | |

{ | |

const Params *params = safe_cast<const Params *>(storage_params); | |

assert(params->type == Hist); | |

data.type = params->type; | |

data.min = min_bucket; | |

data.max = max_bucket + bucket_size - 1; | |

data.bucket_size = bucket_size; | |

data.min_val = min_bucket; | |

data.max_val = max_bucket; | |

int buckets = params->buckets; | |

data.cvec.resize(buckets); | |

for (off_type i = 0; i < buckets; ++i) | |

data.cvec[i] = cvec[i]; | |

data.sum = sum; | |

data.logs = logs; | |

data.squares = squares; | |

data.samples = samples; | |

} | |

/** | |

* Reset stat value to default | |

*/ | |

void | |

reset(const StorageParams* const storage_params) | |

{ | |

const Params *params = safe_cast<const Params *>(storage_params); | |

min_bucket = 0; | |

max_bucket = params->buckets - 1; | |

bucket_size = 1; | |

size_type size = cvec.size(); | |

for (off_type i = 0; i < size; ++i) | |

cvec[i] = Counter(); | |

sum = Counter(); | |

squares = Counter(); | |

samples = Counter(); | |

logs = Counter(); | |

} | |

}; | |

/** | |

* Templatized storage and interface for a distribution that calculates mean | |

* and variance. | |

*/ | |

class SampleStor | |

{ | |

private: | |

/** The current sum. */ | |

Counter sum; | |

/** The sum of squares. */ | |

Counter squares; | |

/** The number of samples. */ | |

Counter samples; | |

public: | |

struct Params : public DistParams | |

{ | |

Params() : DistParams(Deviation) {} | |

}; | |

/** | |

* Create and initialize this storage. | |

*/ | |

SampleStor(const StorageParams* const storage_params) | |

: sum(Counter()), squares(Counter()), samples(Counter()) | |

{ } | |

/** | |

* Add a value the given number of times to this running average. | |

* Update the running sum and sum of squares, increment the number of | |

* values seen by the given number. | |

* @param val The value to add. | |

* @param number The number of times to add the value. | |

*/ | |

void | |

sample(Counter val, int number) | |

{ | |

sum += val * number; | |

squares += val * val * number; | |

samples += number; | |

} | |

/** | |

* Return the number of entries in this stat, 1 | |

* @return 1. | |

*/ | |

size_type size() const { return 1; } | |

/** | |

* Return true if no samples have been added. | |

* @return True if no samples have been added. | |

*/ | |

bool zero() const { return samples == Counter(); } | |

void | |

prepare(const StorageParams* const storage_params, DistData &data) | |

{ | |

const Params *params = safe_cast<const Params *>(storage_params); | |

assert(params->type == Deviation); | |

data.type = params->type; | |

data.sum = sum; | |

data.squares = squares; | |

data.samples = samples; | |

} | |

/** | |

* Reset stat value to default | |

*/ | |

void | |

reset(const StorageParams* const storage_params) | |

{ | |

sum = Counter(); | |

squares = Counter(); | |

samples = Counter(); | |

} | |

}; | |

/** | |

* Templatized storage for distribution that calculates per tick mean and | |

* variance. | |

*/ | |

class AvgSampleStor | |

{ | |

private: | |

/** Current total. */ | |

Counter sum; | |

/** Current sum of squares. */ | |

Counter squares; | |

public: | |

struct Params : public DistParams | |

{ | |

Params() : DistParams(Deviation) {} | |

}; | |

/** | |

* Create and initialize this storage. | |

*/ | |

AvgSampleStor(const StorageParams* const storage_params) | |

: sum(Counter()), squares(Counter()) | |

{} | |

/** | |

* Add a value to the distribution for the given number of times. | |

* Update the running sum and sum of squares. | |

* @param val The value to add. | |

* @param number The number of times to add the value. | |

*/ | |

void | |

sample(Counter val, int number) | |

{ | |

sum += val * number; | |

squares += val * val * number; | |

} | |

/** | |

* Return the number of entries, in this case 1. | |

* @return 1. | |

*/ | |

size_type size() const { return 1; } | |

/** | |

* Return true if no samples have been added. | |

* @return True if the sum is zero. | |

*/ | |

bool zero() const { return sum == Counter(); } | |

void | |

prepare(const StorageParams* const storage_params, DistData &data) | |

{ | |

const Params *params = safe_cast<const Params *>(storage_params); | |

assert(params->type == Deviation); | |

data.type = params->type; | |

data.sum = sum; | |

data.squares = squares; | |

data.samples = curTick(); | |

} | |

/** | |

* Reset stat value to default | |

*/ | |

void | |

reset(const StorageParams* const storage_params) | |

{ | |

sum = Counter(); | |

squares = Counter(); | |

} | |

}; | |

/** | |

* Templatized storage and interface for a sparse histogram stat. There | |

* is no actual limit on the number of buckets, and each of them has a size | |

* of 1, meaning that samples are individually recorded, and there is no | |

* need to keep track of the samples that occur in between two distant | |

* sampled values. | |

*/ | |

class SparseHistStor | |

{ | |

private: | |

/** Counter for number of samples */ | |

Counter samples; | |

/** Counter for each bucket. */ | |

MCounter cmap; | |

public: | |

/** The parameters for a sparse histogram stat. */ | |

struct Params : public DistParams | |

{ | |

Params() : DistParams(Hist) {} | |

}; | |

SparseHistStor(const StorageParams* const storage_params) | |

{ | |

reset(storage_params); | |

} | |

/** | |

* Add a value to the distribution for the given number of times. | |

* @param val The value to add. | |

* @param number The number of times to add the value. | |

*/ | |

void | |

sample(Counter val, int number) | |

{ | |

cmap[val] += number; | |

samples += number; | |

} | |

/** | |

* Return the number of buckets in this distribution. | |

* @return the number of buckets. | |

*/ | |

size_type size() const { return cmap.size(); } | |

/** | |

* Returns true if any calls to sample have been made. | |

* @return True if any values have been sampled. | |

*/ | |

bool | |

zero() const | |

{ | |

return samples == Counter(); | |

} | |

void | |

prepare(const StorageParams* const storage_params, SparseHistData &data) | |

{ | |

MCounter::iterator it; | |

data.cmap.clear(); | |

for (it = cmap.begin(); it != cmap.end(); it++) { | |

data.cmap[(*it).first] = (*it).second; | |

} | |

data.samples = samples; | |

} | |

/** | |

* Reset stat value to default | |

*/ | |

void | |

reset(const StorageParams* const storage_params) | |

{ | |

cmap.clear(); | |

samples = 0; | |

} | |

}; | |

} // namespace statistics | |

} // namespace gem5 | |

#endif // __BASE_STATS_STORAGE_HH__ |