aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/MLRsearch/target_stat.py
diff options
context:
space:
mode:
authorVratko Polak <vrpolak@cisco.com>2023-10-19 10:47:48 +0200
committerVratko Polak <vrpolak@cisco.com>2023-10-19 09:38:33 +0000
commit7a27faf661cf54a84ef4ee0984e12879a223ce32 (patch)
tree07b33102eaea074c74a53264934c3fbfdfd4b96d /resources/libraries/python/MLRsearch/target_stat.py
parent351c5e1e92f31465e1a4523d3fe9b7701457a503 (diff)
feat(MLRseach): Update to v8 conditional throughput
Hopefully, with CSIT config values, PDR lower than NDR will not happen. + Bump duration_sum default to an odd number, so users are not surprised by not seeing standard median behavior. For CSIT this should not matter, overheads hide ties and number of trials (at least for STL) should stay the same. Change-Id: Id7130f978c31e71227499612424007c473bcfac2 Signed-off-by: Vratko Polak <vrpolak@cisco.com>
Diffstat (limited to 'resources/libraries/python/MLRsearch/target_stat.py')
-rw-r--r--resources/libraries/python/MLRsearch/target_stat.py59
1 files changed, 47 insertions, 12 deletions
diff --git a/resources/libraries/python/MLRsearch/target_stat.py b/resources/libraries/python/MLRsearch/target_stat.py
index 9d30d51b9c..e558139af7 100644
--- a/resources/libraries/python/MLRsearch/target_stat.py
+++ b/resources/libraries/python/MLRsearch/target_stat.py
@@ -14,7 +14,7 @@
"""Module defining LoadStat class."""
from dataclasses import dataclass, field
-from typing import Tuple
+from typing import Dict, Tuple
from .target_spec import TargetSpec
from .discrete_result import DiscreteResult
@@ -31,12 +31,9 @@ class TargetStat:
or an upper bound. For additional logic for dealing with loss inversion
see MeasurementDatabase.
- Besides the duration sums needed for determining upper and lower bound,
- a field useful for computing the conditional throughput is also included.
- The conditional throughput is average of the (relative) forwarding rates
- of good long trials weighted by gool long trial durations.
- As the intended load is stored elsewhere, the one additional field here
- has a peculiar unit, it is a sum of products of seconds and loss ratios.
+ Also, data needed for conditional throughput is gathered here,
+ exposed only as a pessimistic loss ratio
+ (as the load value is not stored here).
"""
target: TargetSpec = field(repr=False)
@@ -49,8 +46,8 @@ class TargetStat:
"""Sum of durations of shorter trials satisfying target loss ratio."""
bad_short: float = 0.0
"""Sum of durations of shorter trials not satisfying target loss ratio."""
- dur_rat_sum: float = 0.0
- """Sum over good long trials, of duration multiplied by loss ratio."""
+ long_losses: Dict[float, float] = field(repr=False, default_factory=dict)
+ """If a loss ratio value occured in a long trial, map it to duration sum."""
def __str__(self) -> str:
"""Convert into a short human-readable string.
@@ -73,14 +70,18 @@ class TargetStat:
:type result: DiscreteResult
"""
dwo = result.duration_with_overheads
+ rlr = result.loss_ratio
if result.intended_duration >= self.target.trial_duration:
- if result.loss_ratio > self.target.loss_ratio:
+ if rlr not in self.long_losses:
+ self.long_losses[rlr] = 0.0
+ self.long_losses = dict(sorted(self.long_losses.items()))
+ self.long_losses[rlr] += dwo
+ if rlr > self.target.loss_ratio:
self.bad_long += dwo
else:
self.good_long += dwo
- self.dur_rat_sum += dwo * result.loss_ratio
else:
- if result.loss_ratio > self.target.loss_ratio:
+ if rlr > self.target.loss_ratio:
self.bad_short += dwo
else:
self.good_short += dwo
@@ -115,3 +116,37 @@ class TargetStat:
optimistic = effective_excess <= limit_dursum
pessimistic = (effective_dursum - self.good_long) <= limit_dursum
return optimistic, pessimistic
+
+ def pessimistic_loss_ratio(self) -> float:
+ """Return the loss ratio for conditional throughput computation.
+
+ It adds missing dursum as full-loss trials to long_losses
+ and returns a quantile corresponding to exceed ratio.
+ In case of tie (as in median for even number of samples),
+ this returns the lower value (as being equal to goal exceed ratio
+ is allowed).
+
+ For loads classified as a lower bound, the return value
+ ends up being no larger than the target loss ratio.
+ This is because the excess short bad trials would only come
+ after the quantile in question (as would full-loss missing trials).
+ For other loads, anything can happen, but conditional throughput
+ should not be computed for those anyway.
+ Those two facts allow the logic here be simpler than in estimates().
+
+ :returns: Effective loss ratio based on long trial results.
+ :rtype: float
+ """
+ all_long = max(self.target.duration_sum, self.good_long + self.bad_long)
+ remaining = all_long * (1.0 - self.target.exceed_ratio)
+ ret = None
+ for ratio, dursum in self.long_losses.items():
+ if ret is None or remaining > 0.0:
+ ret = ratio
+ remaining -= dursum
+ else:
+ break
+ else:
+ if remaining > 0.0:
+ ret = 1.0
+ return ret