Skip to main content

Sampler

Struct Sampler 

Source
pub struct Sampler { /* private fields */ }
Expand description

A wait-free, lossy circular buffer for capturing telemetry samples.

Sampler provides a high-throughput mechanism for recording numeric data in environments with massive write contention. It prioritizes progress for producers (threads recording data) over absolute data retention.

§Concurrency Design

The buffer is wait-free. It uses a single atomic [head] pointer to reserve slots in the samples vector. Multiple producers can concurrently claim indices and store values without blocking or needing to coordinate beyond a single fetch_add.

§Lossy Buffer Mechanics

Once the buffer reaches its capacity ($2^n$), the head index wraps around, and new samples overwrite the oldest data. This ensures memory usage remains constant regardless of the volume of events.

§Significant Implementation Details

  • Masking: Indexing uses bitwise & mask instead of the remainder operator %. This requires the internal capacity to be a power of two, a constraint enforced during initialization.
  • Zero-Value Sentinels: During aggregation ([write_samples]), values of 0 are ignored. This identifies uninitialized slots or intentionally skipped samples.
  • Read Consistency: Snapshotting iterates over the buffer with Relaxed loads. While a snapshot is being taken, producers may be overwriting values, making the snapshot an “eventually consistent” view of the buffer’s state.

Implementations§

Source§

impl Sampler

Source

pub fn new(capacity: usize) -> Self

Creates a new Sampler with a capacity rounded to the nearest power of two.

The actual capacity is $2^n$, ensuring that bitwise masking can be used for fast index calculation.

Source

pub fn record(&self, data: u64)

Records a value into the circular buffer using a bit-packed generation tag.

This operation is wait-free, ensuring that high-frequency writers are never blocked by concurrent readers or other writers. It uniquely tags each sample with a sequence_id (lap count) to prevent “ghost reads”—where stale data from a previous rotation of the buffer is incorrectly included in a new snapshot.

§Bit-Packing Layout

The 64-bit atomic slot is partitioned to allow single-word updates:

  • Bits 63–48 (16 bits): sequence_id. Acts as a filter to validate data “freshness.”
  • Bits 47–0 (48 bits): data. Supports measurements up to $2^{48} - 1$ (e.g., $\approx 281$ TB or 281 trillion nanoseconds).
§Performance & Safety
  • Efficiency: Uses a bitwise mask (head & mask) for indexing, avoiding expensive integer division. This requires the buffer size to be a power of two.
  • Memory Ordering: Uses Relaxed for the index increment to minimize cache-line contention, while Acquire on the sequence_id ensures the writer is synchronized with the current global lap.
§Examples

Since the value is masked by SAMPLER_VALUE_MASK, any bits higher than 47 provided in the value argument will be truncated to ensure the sequence_id remains uncorrupted.

Source

pub fn capacity(&self) -> usize

Returns the power-of-two capacity of the sampler.

Source

pub fn write_samples(&self, histogram: &mut Histogram<u64>)

Aggregates samples from the buffer that match the generation active at the start of the call.

§Ghost Data Prevention

This method implements a generation-flip snapshot. By atomically incrementing the sequence_id at the entry point, it captures the ID of the just-completed generation. During iteration, it filters the buffer to prevent:

  • Stale Data (Ghosts): Samples with a tag smaller than sequency_id are ignored.
  • Future Data: Samples with a tag larger than sequency_id (from writers that started after this read began) trigger an early break.
§Concurrency & Performance
  • Wait-Free: Writers are never blocked. They simply begin tagging new samples with the next generation ID while this reader processes the previous one.
  • Early Exit: The break condition optimizes for cases where writers rapidly overtake the reader, preventing unnecessary iteration over “future” slots.
  • Memory Ordering: Uses Release on the increment to ensure subsequent writes in the next generation are ordered after this snapshot’s boundary.
§Panics

Does not panic, though it assumes the buffer is not so large that the reader cannot complete a pass before the 16-bit sequence_id wraps around.

Trait Implementations§

Source§

impl Debug for Sampler

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V