-
- logging.info(
- "starting iterations with duration %s and relative width goal %s",
- state.duration, state.width_goal)
- while 1:
- if time.time() > start_time + self.timeout:
- raise RuntimeError("Optimized search takes too long.")
- # Order of priorities: invalid bounds (nl, pl, nh, ph),
- # then narrowing relative Tr widths.
- # Durations are not priorities yet,
- # they will settle on their own hopefully.
- ndr_lo = state.result.ndr_interval.measured_low
- ndr_hi = state.result.ndr_interval.measured_high
- pdr_lo = state.result.pdr_interval.measured_low
- pdr_hi = state.result.pdr_interval.measured_high
- ndr_rel_width = max(
- state.width_goal, state.result.ndr_interval.rel_tr_width)
- pdr_rel_width = max(
- state.width_goal, state.result.pdr_interval.rel_tr_width)
- # If we are hitting maximal or minimal rate, we cannot shift,
- # but we can re-measure.
- new_tr = self._ndrpdr_loss_fraction(state,
- ndr_lo, ndr_hi, pdr_lo, pdr_hi,
- ndr_rel_width, pdr_rel_width)
-
- if new_tr is not None:
- state = self._measure_and_update_state(state, new_tr)
- continue
-
- # If we are hitting maximum_transmit_rate,
- # it is still worth narrowing width,
- # hoping large enough loss fraction will happen.
- # But if we are hitting the minimal rate (at current duration),
- # no additional measurement will help with that,
- # so we can stop narrowing in this phase.
- if (ndr_lo.target_tr <= state.minimum_transmit_rate
- and ndr_lo.loss_fraction > 0.0):
- ndr_rel_width = 0.0
- if (pdr_lo.target_tr <= state.minimum_transmit_rate
- and pdr_lo.loss_fraction > state.packet_loss_ratio):
- pdr_rel_width = 0.0
-
- new_tr = self._ndrpdr_width_goal(state, ndr_lo, pdr_lo,
- ndr_rel_width, pdr_rel_width)
-
- if new_tr is not None:
- state = self._measure_and_update_state(state, new_tr)
- continue
-
- # We do not need to improve width, but there still might be
- # some measurements with smaller duration.
- new_tr = self._ndrpdr_duration(state,
- ndr_lo, ndr_hi, pdr_lo, pdr_hi,
- ndr_rel_width, pdr_rel_width)
-
- if new_tr is not None:
- state = self._measure_and_update_state(state, new_tr)
- continue
-
- # Widths are narrow (or lower bound minimal), bound measurements
- # are long enough, we can return.
- logging.info("phase done")
- break
- return state
-
- def _ndrpdr_loss_fraction(self, state, ndr_lo, ndr_hi, pdr_lo, pdr_hi,
- ndr_rel_width, pdr_rel_width):
- """Perform loss_fraction-based trials within a ndrpdr phase
-
- :param state: current state
- :param ndr_lo: ndr interval measured low
- :param ndr_hi: ndr interval measured high
- :param pdr_lo: pdr interval measured low
- :param pdr_hi: pdr interval measured high
- :param ndr_rel_width: ndr interval relative width
- :param pdr_rel_width: pdr interval relative width
- :type state: ProgressState
- :type ndr_lo: ReceiveRateMeasurement.ReceiveRateMeasurement
- :type ndr_hi: ReceiveRateMeasurement.ReceiveRateMeasurement
- :type pdr_lo: ReceiveRateMeasurement.ReceiveRateMeasurement
- :type pdr_hi: ReceiveRateMeasurement.ReceiveRateMeasurement
- :type ndr_rel_width: float
- :type pdr_rel_width: float
- :returns: a new transmit rate if one should be applied
- :rtype: float
- """
- result = None
- if ndr_lo.loss_fraction > 0.0:
- if ndr_lo.target_tr > state.minimum_transmit_rate:
- result = max(
- state.minimum_transmit_rate,
- self.expand_down(
- ndr_rel_width, self.doublings, ndr_lo.target_tr))
- logging.info("ndr lo external %s", result)
- elif ndr_lo.duration < state.duration:
- result = state.minimum_transmit_rate
- logging.info("ndr lo minimal re-measure")
-
- if result is None and pdr_lo.loss_fraction > state.packet_loss_ratio:
- if pdr_lo.target_tr > state.minimum_transmit_rate:
- result = max(
- state.minimum_transmit_rate,
- self.expand_down(
- pdr_rel_width, self.doublings, pdr_lo.target_tr))
- logging.info("pdr lo external %s", result)
- elif pdr_lo.duration < state.duration:
- result = state.minimum_transmit_rate
- logging.info("pdr lo minimal re-measure")
-
- if result is None and ndr_hi.loss_fraction <= 0.0:
- if ndr_hi.target_tr < state.maximum_transmit_rate:
- result = min(
- state.maximum_transmit_rate,
- self.expand_up(
- ndr_rel_width, self.doublings, ndr_hi.target_tr))
- logging.info("ndr hi external %s", result)
- elif ndr_hi.duration < state.duration:
- result = state.maximum_transmit_rate
- logging.info("ndr hi maximal re-measure")
-
- if result is None and pdr_hi.loss_fraction <= state.packet_loss_ratio:
- if pdr_hi.target_tr < state.maximum_transmit_rate:
- result = min(
- state.maximum_transmit_rate,
- self.expand_up(
- pdr_rel_width, self.doublings, pdr_hi.target_tr))
- logging.info("pdr hi external %s", result)
- elif pdr_hi.duration < state.duration:
- result = state.maximum_transmit_rate
- logging.info("ndr hi maximal re-measure")
- return result
-
- def _ndrpdr_width_goal(self, state, ndr_lo, pdr_lo,
- ndr_rel_width, pdr_rel_width):
- """Perform width_goal-based trials within a ndrpdr phase
-
- :param state: current state
- :param ndr_lo: ndr interval measured low
- :param pdr_lo: pdr interval measured low
- :param ndr_rel_width: ndr interval relative width
- :param pdr_rel_width: pdr interval relative width
- :type state: ProgressState
- :type ndr_lo: ReceiveRateMeasurement.ReceiveRateMeasurement
- :type pdr_lo: ReceiveRateMeasurement.ReceiveRateMeasurement
- :type ndr_rel_width: float
- :type pdr_rel_width: float
- :returns: a new transmit rate if one should be applied
- :rtype: float
- Return a new transmit rate if one should be applied.
- """
- if ndr_rel_width > state.width_goal:
- # We have to narrow NDR width first, as NDR internal search
- # can invalidate PDR (but not vice versa).
- result = self.half_step_up(ndr_rel_width, ndr_lo.target_tr)
- logging.info("Bisecting for NDR at %s", result)
- elif pdr_rel_width > state.width_goal:
- # PDR iternal search.
- result = self.half_step_up(pdr_rel_width, pdr_lo.target_tr)
- logging.info("Bisecting for PDR at %s", result)
+ self.debug(
+ f"Starting phase with {state.duration} duration"
+ f" and {state.width_goal} relative width goal."
+ )
+ 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.