+ failing_fast = False
+ database = state.database
+ database.set_current_duration(state.duration)
+ while time.monotonic() < state.stop_time:
+ for index, ratio in enumerate(state.packet_loss_ratios):
+ new_tr = self._select_for_ratio(ratio)
+ if new_tr is None:
+ # Either this ratio is fine, or min rate got invalid result.
+ # If fine, we will continue to handle next ratio.
+ if index > 0:
+ # First ratio passed, all next have a valid lower bound.
+ continue
+ lower_bound, _, _, _, _, _ = database.get_bounds(ratio)
+ if lower_bound is None:
+ failing_fast = True
+ self.debug(u"No valid lower bound for this iteration.")
+ break
+ # First ratio is fine.
+ continue
+ # We have transmit rate to measure at.
+ # We do not check duration versus stop_time here,
+ # as some measurers can be unpredictably faster
+ # than what duration suggests.
+ measurement = self.measurer.measure(
+ duration=state.duration,
+ transmit_rate=new_tr,
+ )
+ database.add(measurement)
+ # Restart ratio handling on updated database.
+ break
+ else:
+ # No ratio needs measuring, we are done with this phase.
+ self.debug(u"Phase done.")
+ break
+ # We have broken out of the for loop.
+ if failing_fast:
+ # Abort the while loop early.
+ break
+ # Not failing fast but database got updated, restart the while loop.
+ else:
+ # Time is up.
+ raise RuntimeError(u"Optimized search takes too long.")
+ # Min rate is not valid, but returning what we have
+ # so next duration can recover.
+
+ @staticmethod
+ def improves(new_bound, lower_bound, upper_bound):
+ """Return whether new bound improves upon old bounds.
+
+ To improve, new_bound has to be not None,
+ and between the old bounds (where the bound is not None).
+
+ This piece of logic is commonly used, when we know old bounds
+ from a primary source (e.g. current duration database)
+ and new bound from a secondary source (e.g. previous duration database).
+ Having a function allows "if improves(..):" construction to save space.
+
+ :param new_bound: The bound we consider applying.
+ :param lower_bound: Known bound, new_bound has to be higher to apply.
+ :param upper_bound: Known bound, new_bound has to be lower to apply.
+ :type new_bound: Optional[ReceiveRateMeasurement]
+ :type lower_bound: Optional[ReceiveRateMeasurement]
+ :type upper_bound: Optional[ReceiveRateMeasurement]
+ :returns: Whether we can apply the new bound.
+ :rtype: bool
+ """
+ if new_bound is None:
+ return False
+ if lower_bound is not None:
+ if new_bound.target_tr <= lower_bound.target_tr:
+ return False
+ if upper_bound is not None:
+ if new_bound.target_tr >= upper_bound.target_tr:
+ return False
+ return True
+
+ def _select_for_ratio(self, ratio):
+ """Return None or new target_tr to measure at.
+
+ Returning None means either we have narrow enough valid interval
+ for this ratio, or we are hitting min rate and should fail early.
+
+ :param ratio: Loss ratio to ensure narrow valid bounds for.
+ :type ratio: float
+ :returns: The next target transmit rate to measure at.
+ :rtype: Optional[float]
+ :raises RuntimeError: If database inconsistency is detected.
+ """
+ state = self.state
+ data = state.database
+ bounds = data.get_bounds(ratio)
+ cur_lo1, cur_hi1, pre_lo, pre_hi, cur_lo2, cur_hi2 = bounds
+ pre_lo_improves = self.improves(pre_lo, cur_lo1, cur_hi1)
+ pre_hi_improves = self.improves(pre_hi, cur_lo1, cur_hi1)
+ # TODO: Detect also the other case for initial bisect, see below.
+ if pre_lo_improves and pre_hi_improves:
+ # We allowed larger width for previous phase
+ # as single bisect here guarantees only one re-measurement.
+ new_tr = self._bisect(pre_lo, pre_hi)
+ if new_tr is not None:
+ self.debug(f"Initial bisect for {ratio}, tr: {new_tr}")
+ return new_tr
+ if pre_lo_improves:
+ new_tr = pre_lo.target_tr
+ self.debug(f"Re-measuring lower bound for {ratio}, tr: {new_tr}")
+ return new_tr
+ if pre_hi_improves:
+ # This can also happen when we did not do initial bisect
+ # for this ratio yet, but the previous duration lower bound
+ # for this ratio got already re-measured as previous duration
+ # upper bound for previous ratio.
+ new_tr = pre_hi.target_tr
+ self.debug(f"Re-measuring upper bound for {ratio}, tr: {new_tr}")
+ return new_tr
+ if cur_lo1 is None and cur_hi1 is None:
+ raise RuntimeError(u"No results found in databases!")
+ if cur_lo1 is None:
+ # Upper bound exists (cur_hi1).
+ # We already tried previous lower bound.
+ # So, we want to extend down.
+ new_tr = self._extend_down(
+ cur_hi1, cur_hi2, pre_hi, second_needed=False