Skip to content

Commit

Permalink
removed recovery from cubic
Browse files Browse the repository at this point in the history
  • Loading branch information
Aperence committed Dec 13, 2023
1 parent c866921 commit 1d1ea54
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 46 deletions.
3 changes: 0 additions & 3 deletions examples/http3_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,9 +518,6 @@ async def main(
parser.add_argument(
"--zero-rtt", action="store_true", help="try to send requests using 0-RTT"
)
parser.add_argument(
"--congestion-control", type=str, help="which congestion control algorithm to use (reno, cubic)"
)

args = parser.parse_args()

Expand Down
3 changes: 0 additions & 3 deletions examples/http3_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,9 +564,6 @@ async def main(
parser.add_argument(
"-v", "--verbose", action="store_true", help="increase logging verbosity"
)
parser.add_argument(
"--congestion-control", type=str, help="which congestion control algorithm to use (reno, cubic)"
)
args = parser.parse_args()

logging.basicConfig(
Expand Down
7 changes: 3 additions & 4 deletions src/aioquic/quic/congestion/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ class QuicCongestionControl(abc.ABC):
congestion_window: int = 0
ssthresh: Optional[int] = None

def __init__(self, *, max_datagram_size: int, recovery) -> None:
def __init__(self, *, max_datagram_size: int) -> None:
self.congestion_window = K_INITIAL_WINDOW * max_datagram_size
self.recovery = recovery

@abc.abstractmethod
def on_packet_acked(self, *, now : float, packet: QuicSentPacket) -> None:
def on_packet_acked(self, *, packet: QuicSentPacket) -> None:
... # pragma: no cover

@abc.abstractmethod
def on_packet_sent(self, *, now : float, packet: QuicSentPacket) -> None:
def on_packet_sent(self, *, packet: QuicSentPacket) -> None:
... # pragma: no cover

@abc.abstractmethod
Expand Down
44 changes: 23 additions & 21 deletions src/aioquic/quic/congestion/cubic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,34 @@
K_CUBIC_LOSS_REDUCTION_FACTOR = 0.7
K_CUBIC_MAX_IDLE_TIME = 2 # reset the cwnd after 2 seconds of inactivity

def better_cube_root(self, x):
if (x < 0):
# avoid precision errors that make the cube root returns an imaginary number
return -((-x)**(1./3.))
else:
return (x)**(1./3.)

class CubicCongestionControl(QuicCongestionControl):
"""
Cubic congestion control implementation for aioquic
"""

def __init__(self, max_datagram_size : int, recovery) -> None:
super().__init__(max_datagram_size=max_datagram_size, recovery=recovery)
def __init__(self, max_datagram_size : int) -> None:
super().__init__(max_datagram_size=max_datagram_size)
self.additive_increase_factor = max_datagram_size # increase by one segment

self._max_datagram_size = max_datagram_size
self._congestion_recovery_start_time = 0.0

self._rtt_monitor = QuicRttMonitor()

self.rtt = 0.02 # starting RTT is considered to be 20ms

self.reset()

self.last_ack = None

self.now = 0

def better_cube_root(self, x):
if (x < 0):
# avoid precision errors that make the cube root returns an imaginary number
return -((-x)**(1./3.))
else:
return (x)**(1./3.)

def W_cubic(self, t):
W_max_segments = self._W_max / self._max_datagram_size
Expand All @@ -64,11 +66,10 @@ def reset(self):
self._t_epoch = 0
self._W_max = self.congestion_window

def on_packet_acked(self, packet: QuicSentPacket, now: float) -> None:
def on_packet_acked(self, packet: QuicSentPacket) -> None:
self.bytes_in_flight -= packet.sent_bytes
rtt = self.recovery._rtt_smoothed
self.last_ack = now
self.now = now
self.last_ack = packet.sent_time
now = packet.sent_time

if self.ssthresh is None or self.congestion_window < self.ssthresh:
# slow start
Expand All @@ -85,7 +86,7 @@ def on_packet_acked(self, packet: QuicSentPacket, now: float) -> None:
# calculate K
W_max_segments = self._W_max / self._max_datagram_size
cwnd_epoch_segments = self._cwnd_epoch / self._max_datagram_size
self.K = self.better_cube_root((W_max_segments - cwnd_epoch_segments)/K_CUBIC_C)
self.K = better_cube_root((W_max_segments - cwnd_epoch_segments)/K_CUBIC_C)

# initialize the variables used at start of congestion avoidance
if self._starting_congestion_avoidance:
Expand All @@ -97,20 +98,20 @@ def on_packet_acked(self, packet: QuicSentPacket, now: float) -> None:
# calculate K
W_max_segments = self._W_max / self._max_datagram_size
cwnd_epoch_segments = self._cwnd_epoch / self._max_datagram_size
self.K = self.better_cube_root((W_max_segments - cwnd_epoch_segments)/K_CUBIC_C)
self.K = better_cube_root((W_max_segments - cwnd_epoch_segments)/K_CUBIC_C)


self._W_est = self._W_est + self.additive_increase_factor*(packet.sent_bytes/self.congestion_window)

t = now - self._t_epoch

target = None
if (self.W_cubic(t + rtt) < self.congestion_window):
if (self.W_cubic(t + self.rtt) < self.congestion_window):
target = self.congestion_window
elif (self.W_cubic(t + rtt) > 1.5*self.congestion_window):
elif (self.W_cubic(t + self.rtt) > 1.5*self.congestion_window):
target = self.congestion_window*1.5
else:
target = self.W_cubic(t + rtt)
target = self.W_cubic(t + self.rtt)


if self.is_reno_friendly(t):
Expand All @@ -123,12 +124,12 @@ def on_packet_acked(self, packet: QuicSentPacket, now: float) -> None:
# convex region of cubic (https://www.rfc-editor.org/rfc/rfc9438.html#name-convex-region)
self.congestion_window = self.congestion_window + ((target - self.congestion_window)*(self._max_datagram_size/self.congestion_window))

def on_packet_sent(self, packet: QuicSentPacket, now : float) -> None:
def on_packet_sent(self, packet: QuicSentPacket) -> None:
self.bytes_in_flight += packet.sent_bytes
self.now = now
self.now = packet.sent_time
if self.last_ack == None:
return
elapsed_idle = now - self.last_ack
elapsed_idle = packet.sent_time - self.last_ack
if (elapsed_idle >= K_CUBIC_MAX_IDLE_TIME):
self.reset()

Expand Down Expand Up @@ -170,6 +171,7 @@ def on_packets_lost(self, packets: Iterable[QuicSentPacket], now: float) -> None

def on_rtt_measurement(self, latest_rtt: float, now: float) -> None:
self.now = now
self.rtt = latest_rtt
# check whether we should exit slow start
if self.ssthresh is None and self._rtt_monitor.is_rtt_increasing(
rtt=latest_rtt, now=now
Expand Down
8 changes: 4 additions & 4 deletions src/aioquic/quic/congestion/reno.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ class RenoCongestionControl(QuicCongestionControl):
New Reno congestion control.
"""

def __init__(self, *, max_datagram_size: int, recovery) -> None:
super().__init__(max_datagram_size=max_datagram_size, recovery=recovery)
def __init__(self, *, max_datagram_size: int) -> None:
super().__init__(max_datagram_size=max_datagram_size)
self._max_datagram_size = max_datagram_size
self._congestion_recovery_start_time = 0.0
self._congestion_stash = 0
self._rtt_monitor = QuicRttMonitor()

def on_packet_acked(self, *, now: float, packet: QuicSentPacket) -> None:
def on_packet_acked(self, *, packet: QuicSentPacket) -> None:
self.bytes_in_flight -= packet.sent_bytes

# don't increase window in congestion recovery
Expand All @@ -41,7 +41,7 @@ def on_packet_acked(self, *, now: float, packet: QuicSentPacket) -> None:
self._congestion_stash -= count * self.congestion_window
self.congestion_window += count * self._max_datagram_size

def on_packet_sent(self, packet: QuicSentPacket, now : float) -> None:
def on_packet_sent(self, packet: QuicSentPacket) -> None:
self.bytes_in_flight += packet.sent_bytes

def on_packets_expired(self, packets: Iterable[QuicSentPacket]) -> None:
Expand Down
2 changes: 1 addition & 1 deletion src/aioquic/quic/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ def datagrams_to_send(self, now: float) -> List[Tuple[bytes, NetworkAddress]]:
for packet in packets:
packet.sent_time = now
self._loss.on_packet_sent(
packet=packet, space=self._spaces[packet.epoch], now=now
packet=packet, space=self._spaces[packet.epoch]
)
if packet.epoch == tls.Epoch.HANDSHAKE:
sent_handshake = True
Expand Down
8 changes: 4 additions & 4 deletions src/aioquic/quic/recovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def __init__(

# congestion control
self._cc = create_congestion_control(
congestion_control_algorithm, max_datagram_size=max_datagram_size, recovery=self
congestion_control_algorithm, max_datagram_size=max_datagram_size
)
self._pacer = QuicPacketPacer(max_datagram_size=max_datagram_size)

Expand Down Expand Up @@ -203,7 +203,7 @@ def on_ack_received(
is_ack_eliciting = True
space.ack_eliciting_in_flight -= 1
if packet.in_flight:
self._cc.on_packet_acked(packet=packet, now=now)
self._cc.on_packet_acked(packet=packet)
largest_newly_acked = packet_number
largest_sent_time = packet.sent_time

Expand Down Expand Up @@ -267,7 +267,7 @@ def on_loss_detection_timeout(self, *, now: float) -> None:
self._pto_count += 1
self.reschedule_data(now=now)

def on_packet_sent(self, packet: QuicSentPacket, space: QuicPacketSpace, now : float) -> None:
def on_packet_sent(self, packet: QuicSentPacket, space: QuicPacketSpace) -> None:
space.sent_packets[packet.packet_number] = packet

if packet.is_ack_eliciting:
Expand All @@ -277,7 +277,7 @@ def on_packet_sent(self, packet: QuicSentPacket, space: QuicPacketSpace, now : f
self._time_of_last_sent_ack_eliciting_packet = packet.sent_time

# add packet to bytes in flight
self._cc.on_packet_sent(packet, now=now)
self._cc.on_packet_sent(packet)

if self._quic_logger is not None:
self._log_metrics_updated()
Expand Down
8 changes: 2 additions & 6 deletions tests/test_cubic.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
from aioquic.quic.congestion.cubic import CubicCongestionControl, K_CUBIC_C, K_CUBIC_LOSS_REDUCTION_FACTOR, QuicSentPacket
from aioquic.quic.congestion.cubic import CubicCongestionControl, K_CUBIC_C, K_CUBIC_LOSS_REDUCTION_FACTOR, QuicSentPacket, better_cube_root
import unittest

def W_cubic(t, K, W_max):
return K_CUBIC_C * (t - K)**3 + (W_max)

def cube_root(x):
if (x < 0): return -((-x)**(1/3))
else: return x**(1/3)

class RecoveryStaticRtt:
def __init__(self):
self._rtt_smoothed = 0
Expand All @@ -25,7 +21,7 @@ def test_congestion_avoidance(self):
n = 400 # number of ms to check

W_max = 5 # starting W_max
K = cube_root(W_max*(1-K_CUBIC_LOSS_REDUCTION_FACTOR)/K_CUBIC_C)
K = better_cube_root(W_max*(1-K_CUBIC_LOSS_REDUCTION_FACTOR)/K_CUBIC_C)
cwnd = W_max*K_CUBIC_LOSS_REDUCTION_FACTOR

correct = []
Expand Down

0 comments on commit 1d1ea54

Please sign in to comment.