///|
/// Errors raised by a latency measurement window.
pub suberror LatencyError {
InvalidCapacity
InvalidTimestamp
InvalidPercentile
} derive(Debug)
///|
/// One completed request/response timing sample.
pub struct LatencySample {
sequence : UInt
created_us : UInt64
completed_us : UInt64
payload_bytes : Int
}
///|
/// A summary of samples currently retained by a latency window.
pub struct LatencyStats {
count : Int
minimum_us : UInt64?
maximum_us : UInt64?
average_us : Double
p95_us : UInt64?
jitter_us : UInt64
payload_bytes : Int
}
///|
/// A bounded rolling timing window for transport and diagnostic paths.
pub struct LatencyWindow {
capacity : Int
mut next_sequence : UInt
samples : Array[LatencySample]
mut last_completed_us : UInt64?
}
///|
pub fn new_latency_window(capacity : Int) -> LatencyWindow raise LatencyError {
if capacity < 0 {
raise InvalidCapacity
}
{ capacity, next_sequence: 1, samples: [], last_completed_us: None }
}
///|
/// Record a completed sample and return its sequence number.
pub fn LatencyWindow::record(
self : LatencyWindow,
created_us : UInt64,
completed_us : UInt64,
payload_bytes : Int,
) -> UInt raise LatencyError {
if completed_us < created_us || payload_bytes < 0 {
raise InvalidTimestamp
}
match self.last_completed_us {
Some(previous) => if completed_us < previous { raise InvalidTimestamp }
None => ()
}
if self.capacity > 0 && self.samples.length() >= self.capacity {
ignore(self.samples.remove(0))
}
let sequence = self.next_sequence
self.next_sequence = if sequence == 0xFFFFFFFF { 1 } else { sequence + 1 }
self.samples.push({ sequence, created_us, completed_us, payload_bytes })
self.last_completed_us = Some(completed_us)
sequence
}
///|
/// Calculate a stable summary of the retained timing samples.
pub fn LatencyWindow::stats(self : LatencyWindow) -> LatencyStats {
if self.samples.is_empty() {
return {
count: 0,
minimum_us: None,
maximum_us: None,
average_us: 0.0,
p95_us: None,
jitter_us: 0,
payload_bytes: 0,
}
}
let durations : Array[UInt64] = []
let mut total : UInt64 = 0
let mut payload_bytes = 0
for sample in self.samples {
let duration = sample.completed_us - sample.created_us
durations.push(duration)
total += duration
payload_bytes += sample.payload_bytes
}
durations.sort()
let count = durations.length()
let p95_index = (count * 95 + 99) / 100 - 1
let minimum = durations[0]
let maximum = durations[count - 1]
{
count,
minimum_us: Some(minimum),
maximum_us: Some(maximum),
average_us: total.to_double() / count.to_double(),
p95_us: Some(durations[p95_index]),
jitter_us: maximum - minimum,
payload_bytes,
}
}
///|
/// Return an arbitrary percentile using nearest-rank selection.
pub fn LatencyWindow::percentile(
self : LatencyWindow,
percent : Int,
) -> UInt64 raise LatencyError {
if percent < 0 || percent > 100 || self.samples.is_empty() {
raise InvalidPercentile
}
let values : Array[UInt64] = []
for sample in self.samples {
values.push(sample.completed_us - sample.created_us)
}
values.sort()
let rank = ((values.length() * percent + 99) / 100).max(1)
values[rank - 1]
}
///|
pub fn LatencyWindow::length(self : LatencyWindow) -> Int {
self.samples.length()
}
///|
pub fn LatencyWindow::capacity(self : LatencyWindow) -> Int {
self.capacity
}
///|
pub fn LatencyWindow::samples(self : LatencyWindow) -> Array[LatencySample] {
self.samples.copy()
}
///|
pub fn LatencySample::sequence(self : LatencySample) -> UInt {
self.sequence
}
///|
pub fn LatencySample::created_at(self : LatencySample) -> UInt64 {
self.created_us
}
///|
pub fn LatencySample::completed_at(self : LatencySample) -> UInt64 {
self.completed_us
}
///|
pub fn LatencySample::duration(self : LatencySample) -> UInt64 {
self.completed_us - self.created_us
}
///|
pub fn LatencySample::payload_bytes(self : LatencySample) -> Int {
self.payload_bytes
}
///|
pub fn LatencyStats::count(self : LatencyStats) -> Int {
self.count
}
///|
pub fn LatencyStats::minimum(self : LatencyStats) -> UInt64? {
self.minimum_us
}
///|
pub fn LatencyStats::maximum(self : LatencyStats) -> UInt64? {
self.maximum_us
}
///|
pub fn LatencyStats::average(self : LatencyStats) -> Double {
self.average_us
}
///|
pub fn LatencyStats::p95(self : LatencyStats) -> UInt64? {
self.p95_us
}
///|
pub fn LatencyStats::jitter(self : LatencyStats) -> UInt64 {
self.jitter_us
}
///|
pub fn LatencyStats::payload_bytes(self : LatencyStats) -> Int {
self.payload_bytes
}
///|
/// Calculate payload throughput over an observation interval.
pub fn LatencyStats::throughput_bytes_per_second(
self : LatencyStats,
duration_us : UInt64,
) -> Double {
if duration_us == 0 {
0.0
} else {
self.payload_bytes.to_double() * 1_000_000.0 / duration_us.to_double()
}
}
///|
/// Render a compact, stable summary for benchmark artifacts.
pub fn LatencyStats::to_text(self : LatencyStats) -> String {
"samples=\{self.count} average_us=\{self.average_us} jitter_us=\{self.jitter_us} payload_bytes=\{self.payload_bytes}"
}
///|
/// Return the newest retained sample, if any.
pub fn LatencyWindow::latest(self : LatencyWindow) -> LatencySample? {
if self.samples.is_empty() {
None
} else {
Some(self.samples[self.samples.length() - 1])
}
}
///|
/// Return the oldest retained sample, if any.
pub fn LatencyWindow::oldest(self : LatencyWindow) -> LatencySample? {
if self.samples.is_empty() {
None
} else {
Some(self.samples[0])
}
}
///|
pub fn LatencyWindow::is_empty(self : LatencyWindow) -> Bool {
self.samples.is_empty()
}
///|
pub fn LatencyStats::has_data(self : LatencyStats) -> Bool {
self.count > 0
}