Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5131e96
feat: cache timestamps per packet
datagutt Jan 5, 2026
0a07cef
feat(toggles): add ToggleSnapshot for efficient toggle state access
datagutt Jan 5, 2026
99e8c67
refactor: optimize get_score method with always inline attribute
datagutt Jan 5, 2026
6bc5cea
refactor: optimize toggle state access by caching snapshots in run_se…
datagutt Jan 5, 2026
d0c84c5
refactor: add inline attribute to is_timed_out and calculate_quality_…
datagutt Jan 5, 2026
79a6c6f
refactor: add inline attribute to functions for optimization
datagutt Jan 5, 2026
bc42b8b
refactor: replace HashMap and VecDeque with a zero-allocation ring bu…
datagutt Jan 5, 2026
ae3ee1e
Create PERFORMANCE_OPTIMIZATION_PLAN.md
datagutt Jan 5, 2026
bd7e5a1
refactor: replace packet_log array with FxHashMap for improved perfor…
datagutt Jan 5, 2026
afcb638
refactor: implement cached quality multiplier for performance optimiz…
datagutt Jan 5, 2026
0ab966d
refactor: pass local_listener to packet processing functions for impr…
datagutt Jan 5, 2026
d9970bb
refactor: allow too many arguments in process_packet_internal for imp…
datagutt Jan 5, 2026
48c8196
fix(connection): send keepalive every IDLE_TIME (1s) unconditionally …
datagutt Jan 6, 2026
efd7313
refactor: initialize last_keepalive_sent to None in test connection h…
datagutt Jan 7, 2026
1170df0
refactor(tests): fix keepalive logic in test_keepalive_needs due to p…
datagutt Jan 7, 2026
ff41c8b
fix(congestion): allow window recovery for connections with no NAK hi…
datagutt Jan 7, 2026
771395f
feat(batch_send): implement batch sending module
datagutt Jan 7, 2026
5339fd4
feat(connection): add batch sender for optimized packet transmission
datagutt Jan 7, 2026
a9c3c73
refactor(sender): remove dead code and implement periodic batch flush…
datagutt Jan 7, 2026
064d547
feat(connection): implement batch receive and update socket handling …
datagutt Jan 7, 2026
3a84ecc
refactor(connection): optimize cumulative ACK processing and enhance …
datagutt Jan 7, 2026
1e047a3
refactor(sender): optimize connection status logging and reduce CPU o…
datagutt Jan 7, 2026
3985299
refactor(packet_handler): optimize packet draining and flushing to re…
datagutt Jan 7, 2026
a35a7c1
refactor(uplink): increase retry pause duration to reduce CPU-intensi…
datagutt Jan 7, 2026
768cc35
refactor(Cargo.toml): reorder libc dependency for better organization
datagutt Jan 7, 2026
9617ac5
refactor(batch_recv): update documentation and configuration for Linu…
datagutt Jan 7, 2026
0b00369
refactor(batch_send): improve error handling during packet sending
datagutt Jan 7, 2026
298fb9b
refactor(batch_recv): update fallback implementation to use socket2::…
datagutt Jan 7, 2026
9ad4fa6
refactor(batch_recv): add missing cfg attribute for BATCH_RECV_SIZE a…
datagutt Jan 7, 2026
69d015a
refactor(connection, test_helpers): replace std::net::UdpSocket with …
datagutt Jan 7, 2026
0c2248f
chore: fmt
datagutt Jan 7, 2026
76b970f
chore: add AGENTS.md reference in CLAUDE.md
datagutt Jan 10, 2026
532d920
refactor(.gitignore): add /.claude to ignore list and ensure newline …
datagutt Jan 10, 2026
d49943a
refactor(test_helpers): host the NEXT_TEST_CONN_ID counter to module …
datagutt Jan 10, 2026
66adf0f
refactor(SrtlaConnection): simplify connection and reconnection logic…
datagutt Jan 10, 2026
c795119
Refactoring
datagutt Jan 10, 2026
c8caf0c
refactor(sender): consolidate event loop logic in run_sender_with_tog…
datagutt Jan 10, 2026
d518e04
refactor(test_helpers): streamline connection creation by consolidati…
datagutt Jan 10, 2026
12cc6bb
refactor(connection): consolidate connection state reset logic in mar…
datagutt Jan 10, 2026
ceab1c8
refactor(reconnection): consolidate backoff delay logic into a single…
datagutt Jan 10, 2026
6f84513
refactor(ack_nak): simplify window update logic by removing redundant…
datagutt Jan 10, 2026
e77a9c3
docs: remove performance optimization plan document - we implemented it
datagutt Jan 10, 2026
c85e655
fix(connection): potential sequence wraparound issue in cumulative AC…
datagutt Jan 10, 2026
0ce64f4
fix(sender): deduplicate new_ips before connecting to avoid duplicate…
datagutt Jan 10, 2026
fd678c7
chore(version): update package version to 3.0.0
datagutt Jan 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ target/
*.c
*.h
bond-bunny-main/
moblin/
moblin/
/.claude
1 change: 1 addition & 0 deletions CLAUDE.md
10 changes: 9 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
resolver = "3"
name = "srtla_send"
version = "2.8.0"
version = "3.0.0"
edition = "2024"
description = "SRT transport proxy with link aggregation for connection bonding (sender-side)"
license = "MIT"
Expand All @@ -17,6 +17,7 @@ rust-version = "1.87"
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"] }
rand = "0.9"
rustc-hash = "2.1"
tokio = { version = "1.48.0", features = [
"rt-multi-thread",
"macros",
Expand All @@ -36,6 +37,9 @@ mimalloc = { version = "0.1.43", default-features = false, features = [
"secure",
] }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[features]
test-internals = []

Expand Down
110 changes: 110 additions & 0 deletions src/connection/ack_nak.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::cmp::min;

use super::SrtlaConnection;
use crate::protocol::*;
use crate::utils::now_ms;

impl SrtlaConnection {
/// Register a packet as in-flight. O(1) insert.
#[inline]
pub fn register_packet(&mut self, seq: i32, send_time_ms: u64) {
self.packet_log.insert(seq, send_time_ms);
self.in_flight_packets = self.packet_log.len() as i32;
}

/// Handle SRT cumulative ACK - clears all packets with seq <= ack.
///
/// Optimized to avoid redundant work:
/// - Tracks highest_acked_seq to skip already-processed ACKs
/// - Only removes packets in the range (highest_acked_seq, ack]
/// - O(k) where k is packets in range, not O(n) for entire log
pub fn handle_srt_ack(&mut self, ack: i32) {
// Skip if this ACK doesn't advance our highest acked sequence
// This handles duplicate ACKs and out-of-order ACKs efficiently
if ack <= self.highest_acked_seq {
return;
}

// Get send time for RTT calculation before removing
let ack_send_time_ms = self.packet_log.get(&ack).copied();

// Remove packets in the range (highest_acked_seq, ack]
// This is more efficient than retain() when ACKs arrive in order
// because we only iterate over the newly-ACKed range
let old_highest = self.highest_acked_seq;
self.highest_acked_seq = ack;

// For small ranges, use targeted removal (O(k) where k = range size)
// For large gaps (e.g., after reconnect), fall back to retain (O(n))
// Note: Wraparound is handled by falling back to retain() for large ranges.
// With typical RTTs and packet rates, wraparound within 64 packets is extremely unlikely.
let range_size = (ack as i64 - old_highest as i64).unsigned_abs();
if range_size <= 64 && old_highest != i32::MIN {
// Targeted removal for small ranges - iterate the range, not the map
for seq in (old_highest + 1)..=ack {
self.packet_log.remove(&seq);
}
} else {
// Fall back to retain for large gaps or initial state
self.packet_log.retain(|&seq, _| seq > ack);
}
self.in_flight_packets = self.packet_log.len() as i32;

// Update RTT estimate if we found the acked packet
if let Some(sent_ms) = ack_send_time_ms {
let now = now_ms();
let rtt = now.saturating_sub(sent_ms);
if rtt > 0 && rtt <= 10_000 {
self.rtt.update_estimate(rtt);
}
}
}

/// Handle NAK for a specific sequence. O(1) remove.
#[inline]
pub fn handle_nak(&mut self, seq: i32) -> bool {
let found = self.packet_log.remove(&seq).is_some();
if found {
self.in_flight_packets = self.packet_log.len() as i32;
self.congestion
.handle_nak(&mut self.window, seq, &self.label);
}
found
}

/// Handle SRTLA ACK for a specific sequence. O(1) remove.
#[inline]
pub fn handle_srtla_ack_specific(&mut self, seq: i32, classic_mode: bool) -> bool {
let found = self.packet_log.remove(&seq).is_some();
if found {
self.in_flight_packets = self.packet_log.len() as i32;

if classic_mode {
self.congestion.handle_srtla_ack_specific_classic(
&mut self.window,
self.in_flight_packets,
seq,
&self.label,
);
} else {
self.congestion.handle_srtla_ack_enhanced(
&mut self.window,
self.in_flight_packets,
&self.label,
);
}
}
found
}

pub fn handle_srtla_ack_global(&mut self) {
// Global +1 window increase for connections that have received data (from
// original implementation)
// This matches C version: if (c->last_rcvd != 0)
// In Rust, we check if last_received is Some (i.e., has been set when data was
// received)
if self.connected && self.last_received.is_some() {
self.window = min(self.window + 1, WINDOW_MAX * WINDOW_MULT);
}
}
}
Loading