21#include <unordered_map>
45 std::unordered_map<KeyType, Entry>
entries;
46 std::unordered_map<KeyType, UndoRecord>
undo;
63 size_t spilled,
bool overflowed,
bool memoryLimited)
81 const auto sample =
static_cast<float>(double(emitted) / scale);
82 e.ratio = firstSample ? sample : (
cfg.
alpha * sample) + ((1.f -
cfg.
alpha) * e.ratio);
95 const float shortfall = granted ?
static_cast<float>(double(emitted) / double(granted)) :
cfg.
marginUp;
100 const float util = granted ? float(
double(emitted) /
double(granted)) : 1.f;
120 std::lock_guard lock{mImpl->mutex};
121 mImpl->entries.clear();
123 mImpl->transactionActive =
false;
128 std::lock_guard lock{mImpl->mutex};
129 if (mImpl->transactionActive) {
130 throw std::logic_error{
"CapacityEstimator transaction already active"};
132 assert(mImpl->undo.empty());
133 mImpl->transactionActive =
true;
138 std::lock_guard lock{mImpl->mutex};
140 mImpl->transactionActive =
false;
145 std::lock_guard lock{mImpl->mutex};
146 if (!mImpl->transactionActive) {
149 for (
const auto& [
key, record] : mImpl->undo) {
150 if (record.existed) {
151 const auto current = mImpl->entries.find(
key);
152 assert(current != mImpl->entries.end());
153 current->second = record.previous;
155 mImpl->entries.erase(
key);
159 mImpl->transactionActive =
false;
167 std::lock_guard lock{mImpl->mutex};
168 const auto it = mImpl->entries.find(
key);
169 if (it == mImpl->entries.end() || it->second.statistics.samples == 0) {
170 return mImpl->cfg.floorSlots;
172 const auto& e = it->second;
173 const double raw = double(e.ratio) * scale * double(e.margin);
174 if (!std::isfinite(
raw) ||
raw < 0.) {
175 return mImpl->cfg.floorSlots;
181 const size_t ceiling = std::max(mImpl->cfg.floorSlots,
static_cast<size_t>(
double(e.statistics.maxEmitted) *
double(mImpl->cfg.marginMax)));
182 if (
raw >=
static_cast<double>(ceiling)) {
185 return std::max(mImpl->cfg.floorSlots,
static_cast<size_t>(std::ceil(
raw)));
190 std::lock_guard lock{mImpl->mutex};
191 const auto it = mImpl->entries.find(
key);
192 if (it == mImpl->entries.end() || it->second.statistics.maxEmitted == 0) {
193 return mImpl->cfg.floorSlots;
195 const auto& e = it->second;
196 const double raw = double(e.statistics.maxEmitted) * double(e.margin);
197 if (!std::isfinite(
raw) ||
raw >=
static_cast<double>(std::numeric_limits<size_t>::max())) {
198 return std::numeric_limits<size_t>::max();
200 return std::max(mImpl->cfg.floorSlots,
static_cast<size_t>(std::ceil(
raw)));
208 std::lock_guard lock{mImpl->mutex};
209 const auto it = mImpl->entries.find(
key);
210 if (it == mImpl->entries.end() || it->second.statistics.samples == 0) {
213 const double raw = double(it->second.ratio) * scale;
214 return std::isfinite(
raw) &&
raw > 0. ?
raw : 0.;
219 std::lock_guard lock{mImpl->mutex};
220 const auto it = mImpl->entries.find(
key);
221 if (it == mImpl->entries.end()) {
224 return it->second.statistics;
232 std::lock_guard lock{mImpl->mutex};
233 mImpl->observe(
key, scale, capacityUsed, capacityUsed, emitted,
234 overflowed && emitted > capacityUsed ? emitted - capacityUsed : 0,
235 overflowed, memoryLimited);
239 size_t emitted,
size_t spilled,
bool overflowed,
bool memoryLimited)
244 std::lock_guard lock{mImpl->mutex};
245 mImpl->observe(
key, scale, requested, granted, emitted, spilled, overflowed, memoryLimited);
250 std::lock_guard lock{mImpl->mutex};
251 std::vector<KeyType> keys;
252 keys.reserve(mImpl->entries.size());
253 for (
const auto& [
key, _] : mImpl->entries) {
257 const auto da = decodeKey(a);
258 const auto db = decodeKey(b);
259 return std::tie(da.site, da.iteration, da.variant, da.slot) <
260 std::tie(db.site, db.iteration, db.variant, db.slot);
265 LOGP(info,
"Printing CapacityEstimators:");
266 for (
const auto key : keys) {
267 const auto&
value = mImpl->entries.at(
key);
270 LOGP(info,
"\tSite:{} | iter:{} | var:({},{}) | slot:{} | ratio:{} | margin:{} | maxEmitted:{} | samples:{} | low:{} | requested:{} | granted:{} | emitted:{} | spilled:{} | overflows:{}",
SlabSiteNames[decoded.site], decoded.iteration,
getVariantHigh(decoded.variant),
getVariantLow(decoded.variant), decoded.slot,
value.ratio,
value.margin,
statistics.
maxEmitted,
statistics.
samples,
statistics.
nLowStreak,
statistics.
requested,
statistics.
granted,
statistics.
emitted,
statistics.
spilled,
statistics.
overflowEvents);
Cross-timeframe output-size prediction.
o2::raw::RawFileWriter * raw
void commitTransaction() noexcept
static constexpr int getVariantHigh(int variant) noexcept
void rollbackTransaction() noexcept
Statistics statistics(uint64_t key) const
size_t capacity(uint64_t key, double scale) const
size_t peakCapacity(uint64_t key) const
static constexpr int getVariantLow(int variant) noexcept
double expected(uint64_t key, double scale) const
void update(uint64_t key, double scale, size_t emitted, size_t capacityUsed, bool overflowed, bool memoryLimited)
static constexpr Decoded decodeKey(KeyType key) noexcept
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
GLboolean GLboolean GLboolean GLboolean a
constexpr const char *const SlabSiteNames[SlabSite::NSlabSite]
float marginOverflowSlack
std::unordered_map< KeyType, Entry > entries
void observe(KeyType key, double scale, size_t requested, size_t granted, size_t emitted, size_t spilled, bool overflowed, bool memoryLimited)
std::unordered_map< KeyType, UndoRecord > undo
void checkpointBeforeUpdate(KeyType key)