Skip to content

xfoa/Impatience

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Impatience

A Rust library for measuring event-to-event latency across networked peers with automatic clock synchronisation.

The library is I/O-agnostic: it has zero dependencies on std::net or threading. Bring your own UDP socket and event loop. Two example CLI programs (sync-test and latency-demo) are included to demonstrate real-world usage.

Features

  • Four abstraction levels from high-level instrumentation down to the raw synchronisation algorithm
  • UDP-based clock sync using a windowed-minimum one-way-delay estimator with 24-bit truncated timestamps
  • Zero-copy serialisation of packet types via rkyv
  • Non-blocking handshake (Initiator / Responder) for establishing peer clocks
  • no_std support at the timesync algorithm layer
  • Optional features: serde, uncertainty, svg (see below)

Quick Start

use impatience::clocks::PeerClock;
use impatience::instrumentation::Profiler;
use impatience::time;

let clock = PeerClock::new();
clock.start(time::now_usec());
let profiler = Profiler::new(clock.clone());

let mut span = profiler.start("input-to-print", clock.local_ms());
let remote_finish_ms = clock.local_ms() + 50;

if let Some(latency_ms) = profiler.finish_remote(&mut span, remote_finish_ms) {
    println!("Latency: {} ms", latency_ms);
}

For a complete networked example with handshake and sync scheduling, see the crate-level documentation (cargo doc --open).

CLI Tools

impatience ships with two example programs.

sync-test

Debug clock synchronisation between two machines over UDP.

# Server
impatience sync-test --server 0.0.0.0 --port 7340

# Client
impatience sync-test --client 192.168.1.5 --port 7340 \
    --count 100 --interval 400 --sync-interval 2000

latency-demo

Measure end-to-end event latency. The client sends keyboard input to the server after a random delay; the server echoes a completion timestamp. Results are printed to the terminal and saved as an HTML report.

# Server
impatience latency-demo --server 0.0.0.0 --port 7341

# Client
impatience latency-demo --client 192.168.1.5 --port 7341 \
    --sync-interval 2000 --max-delay 100

Library Overview

Module Purpose
instrumentation Profiler, Span, LatencyAggregator
clocks PeerClock (thread-safe), SyncedClock (raw), formatting helpers
net Handshake (Initiator / Responder), SyncScheduler, packet types
timesync TimeSynchroniser algorithm and rollover-safe Counter24
time Wall-clock time utilities

Level 1: Instrumentation

Use Profiler and PeerClock for application-level latency tracking. Profiler::start creates a Span; Profiler::finish_remote records the latency when a remote completion timestamp arrives. LatencyAggregator and Snapshot provide percentile statistics and console reporting.

Level 2: Network Protocol

Use net::Initiator and net::Responder for the two-way handshake that establishes peer start times. SyncScheduler tracks when to emit periodic sync heartbeats. Packet types (Packet, StartClockPacket, SyncPacket, etc.) are archived with rkyv and serialised via Packet::to_bytes and Packet::from_bytes.

Level 3: Clock Primitives

Use SyncedClock when you need raw probe and sync update methods plus correction values without the thread-safe PeerClock wrapper. It is single-threaded and does not track peer start times; callers must manage thread safety and peer-start tracking themselves.

Level 4: Algorithm Core

Use TimeSynchroniser and Counter24 to study or extend the windowed-minimum one-way delay algorithm and rollover-safe fixed-bit-width counter arithmetic. This layer is suitable for porting the algorithm to other languages or experimenting with custom windowing strategies.

Interoperability

Wire Format

Built-in packet types are serialised with rkyv. Non-Rust peers must either link an rkyv deserializer or parse the archived bytes directly.

Custom formats (JSON, Protobuf, etc.) are supported by implementing the Probe and PeerSync traits. PeerClock and SyncedClock work with any type that implements these traits, so the built-in packet types are optional.

Protocol

Clock synchronisation runs over UDP in two phases:

  1. Handshake: Client sends StartClock, server replies with AckStartClock.
  2. Periodic sync: Both peers exchange SyncPacket containing a 24-bit truncated local timestamp and a minimum one-way-delay estimate. The receiver expands the truncated timestamp back to 64 bits using rollover-safe Counter24 arithmetic.

Thread Safety

PeerClock is Clone + Send + Sync (backed by Arc<Mutex<_>>). The lower-level types (TimeSynchroniser, SyncedClock, WindowedMinTS24) are single-threaded.

Cargo Features

Feature Default Description
std yes Enables time, clocks, net, instrumentation, and rkyv packet serialisation
alloc implied by std Foundation for no_std environments with heap allocation
cli yes Builds the impatience binary with the sync-test and latency-demo tools
svg yes SVG chart generation (histogram_svg, scatter_plot_svg)
serde yes Serialize/Deserialize derives on select types (e.g. Snapshot)
uncertainty no Statistical confidence intervals via statrs

Use in no_std environments (only the timesync module is available):

[dependencies]
impatience = { version = "1.0", default-features = false }

Enable the uncertainty feature for statistical confidence intervals:

[dependencies]
impatience = { version = "1.0", features = ["uncertainty"] }

The CLI tools can be installed with:

cargo install impatience

License

GPL-3.0, see LICENSE.md for details.

Acknowledgements

This project used the TimeSync library by Chris Taylor as a basis. Indeed, the timesync module is more or less a straight port of that library to Rust.

About

A library for instrumentation of event-to-event latency over a network

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages