diff options
| author | eunhhu <[email protected]> | 2026-09-29 09:49:35 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-29 09:49:35 +0100 |
| commit | 17e54aaa3b08f62d9a5488656d5ef8668d76e571 (patch) | |
| tree | d34f1154f0c5576832850ad26498c49b106faa9b /link | |
| parent | 04fa30f5a321f3ce468e02f74851ebb6f795344e (diff) | |
| download | pure-17e54aaa3b08f62d9a5488656d5ef8668d76e571.tar.gz pure-17e54aaa3b08f62d9a5488656d5ef8668d76e571.zip | |
Pace a link's packets, and keep Reno's pace by time as well as by acknowledgments
Packets go at the window an RTT, a quarter more (twice in slow start),
up to 2 ms of it at once (10 to 256 datagrams, RFC 9002 7.7); the sender
says when the next may go. The Reno-friendly estimate grows by the
acknowledgments (RFC 9438) or by time (RFC 8312), whichever is ahead, so
a link its app holds back to a few records a second does not fall
behind it.
In the M6 simulator (RTT 100 ms, 5% loss, 50 records a second) the
median time a record takes falls from 134 to 51-71 ms and the p99 from
about 900 to 290-350 ms; a backlog with 1% loss at RTT 20 ms goes at
8.3 Mbit/s. The transport's CPU a record is unchanged.
Diffstat (limited to 'link')
| -rw-r--r-- | link/link-protocol.c | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/link/link-protocol.c b/link/link-protocol.c index 226d238..d685966 100644 --- a/link/link-protocol.c +++ b/link/link-protocol.c @@ -221,6 +221,12 @@ struct PureLinkSender { /* The window went unused (nothing to send, or no credit) when the * sender last looked: it does not grow then (RFC 9002 §7.8). */ bool unused; + /* Pacing (RFC 9002 §7.7): bytes it may send now, filled as time goes + * at the window an RTT (a quarter more; twice in slow start), up to 2 ms + * of it (10 to 256 datagrams); when that was last done; when there will + * be enough for the next packet, while it waits for that. */ + double pace_bytes; + uint64_t pace_at, pace_due; /* The datagram's size now; a probe of the largest flies; when the next * may go, and how many were lost (RFC 8899). */ uint32_t datagram; @@ -522,7 +528,12 @@ static void grow(PureLinkSender *l, uint64_t bytes, uint64_t now) { l->cubic_k = 0; } double t = (double)(now - l->cubic_epoch) / 1e9, rtt = (double)(l->sampled ? l->smoothed_rtt : FIRST_RTT) / 1e9; + /* Reno's pace: as the acknowledgments come (RFC 9438), or as time goes + * (RFC 8312), whichever is ahead: a link its app holds back (a few + * records a second) does not fall behind it for that. */ l->cubic_reno += (l->cubic_reno < l->cubic_max ? 3 * (1 - CUBIC_BETA) / (1 + CUBIC_BETA) : 1) * acked / w; + double by_time = l->cubic_max * CUBIC_BETA + 3 * (1 - CUBIC_BETA) / (1 + CUBIC_BETA) * t / rtt; + if (by_time > l->cubic_reno) l->cubic_reno = by_time; double d = t - l->cubic_k, at = CUBIC_C * d * d * d + l->cubic_max; d += rtt; double target = CUBIC_C * d * d * d + l->cubic_max; @@ -734,6 +745,7 @@ static size_t transmit(PureLinkSender *l, Flight *f, const unsigned char *body, } push_flight(l, f); ++l->counts.packets; + l->pace_bytes = l->pace_bytes > (double)total ? l->pace_bytes - (double)total : 0; if (f->eliciting) { ++l->eliciting; l->in_flight += f->bytes; @@ -742,11 +754,28 @@ static size_t transmit(PureLinkSender *l, Flight *f, const unsigned char *body, return total; } +/* Whether pacing lets a packet go now (else when it will, in pace_due). */ +static bool paced(PureLinkSender *l, uint64_t now) { + double rtt = (double)(l->sampled ? l->smoothed_rtt : FIRST_RTT); + double rate = (double)l->window * (l->window < l->threshold ? 2.0 : 1.25) / rtt; /* bytes a ns */ + double most = rate * 2e6, least = 10.0 * l->datagram, top = 256.0 * l->datagram; + most = most < least ? least : most > top ? top : most; + l->pace_bytes += rate * (double)(now - l->pace_at); + if (l->pace_bytes > most) l->pace_bytes = most; + l->pace_at = now; + l->pace_due = 0; + if (l->pace_bytes >= l->datagram) return true; + l->pace_due = now + (uint64_t)(((double)l->datagram - l->pace_bytes) / rate) + 1; + return false; +} + size_t pure_link_sender_packet(PureLinkSender *l, unsigned char out[PURE_LINK_DATAGRAM_MOST], bool whole, uint64_t now, uint64_t *number) { bool probe = l->probes > 0; + l->pace_due = 0; /* set again only when pacing holds a packet back */ if (!probe && l->in_flight + l->datagram > l->window) l->unused = false; if (!l->out || (!probe && l->in_flight + l->datagram > l->window) || !flight_room(l)) return 0; + if (!probe && !paced(l, now)) return 0; unsigned char body[BODY_MOST]; Flight f = {.pn = l->next_pn, .eliciting = true}; if (l->datagram < DATAGRAM_MOST && !l->reaching && l->sampled && now >= l->reach_at && @@ -785,6 +814,7 @@ static bool waiting_for_save(const PureLinkSender *l) { return l->received > 0; uint64_t pure_link_sender_due(const PureLinkSender *l) { uint64_t due = l->loss_time; + if (l->pace_due && (!due || l->pace_due < due)) due = l->pace_due; if (l->eliciting) { uint64_t pto = l->last_eliciting + pto_period(l); if (!due || pto < due) due = pto; |
