Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 27, 2025

Implements double buffering for lock-free sequential updates of object vector collections with memory pooling to reduce allocation overhead. Features immutable, append-only semantics for DataPointSequence ideal for time-series data.

Implementation

ObjectPool

  • Generic pool managing reusable Vec<T> instances
  • Configurable capacity, automatic clearing on release
  • Reduces allocation churn for frequently updated collections

FlatObjectPool

  • Advanced pool using single contiguous buffer with begin/end pointers
  • Tracks vector slices using (begin, end) index pairs
  • Better cache locality and reduced memory fragmentation
  • Auto-extends buffer when needed via set() method
  • Methods: acquire(size), release(begin, end), get_slice(), set()
  • Ideal for high-performance scenarios requiring frequent allocations

DoubleBuffer

  • Front buffer (read) / back buffer (write) separation
  • swap() atomically exchanges buffers and recycles old front buffer via pool
  • Non-blocking reads while preparing next update

DataPointSequence

  • Immutable, append-only semantics: Objects are never modified or erased once added
  • High-level API for managing accumulating sequences of DataPoint objects
  • Uses FlatObjectPool for efficient contiguous storage with begin/end pointers
  • Automatic step tracking with each update() call
  • Sequence grows over time, preserving all historical data points
  • Methods: add_point(), add_points(), update(), current(), step(), pending_count()
  • Ideal for time-series data where complete history must be preserved

Usage

DataPointSequence (High-Level API - Append-Only)

use ry26::{DataPointSequence, DataPoint};

// Create with buffer size 1000 and pool capacity 10
let mut sequence = DataPointSequence::new(1000, 10);

// Step 1: Add initial data point
sequence.add_point(DataPoint {
    id: 1,
    value: 42.0,
    timestamp: "2025-10-27T12:00:00Z".to_string(),
});
sequence.update();
// Current has 1 point

// Step 2: Add more points (accumulates, not replaces!)
sequence.add_points(vec![
    DataPoint { id: 2, value: 84.0, timestamp: "2025-10-27T12:01:00Z".to_string() },
    DataPoint { id: 3, value: 126.0, timestamp: "2025-10-27T12:02:00Z".to_string() },
]);
sequence.update();
// Current now has all 3 points from both steps

let current = sequence.current();
println!("Step {}: {} points", sequence.step(), current.len()); // Step 2: 3 points

FlatObjectPool (Advanced Memory Management)

use ry26::FlatObjectPool;

// Create a flat pool with buffer size 1000 and capacity for 10 free ranges
let mut pool: FlatObjectPool<i32> = FlatObjectPool::new(1000, 10);

// Acquire a slice of 50 elements
let (begin, end) = pool.acquire(50);

// Work with the slice
for i in begin..end {
    pool.set(i, i as i32 * 2);
}

// Get slice reference
let slice = pool.get_slice(begin, end);

// Release back to pool when done
pool.release(begin, end);

DoubleBuffer (Low-Level API)

use ry26::{DoubleBuffer, DataPoint};

let mut buffer: DoubleBuffer<DataPoint> = DoubleBuffer::new(10);

// Write to back buffer
buffer.back_mut().push(data);

// Readers access front while back is being prepared
let current = buffer.front();

// Atomic swap
buffer.swap();

Testing

  • 11 tests covering ObjectPool behavior (capacity limits, reuse, concurrent acquire/release)
  • 14 tests covering FlatObjectPool functionality (acquire/release, reuse, slicing, cache locality, auto-extension)
  • 9 tests covering DoubleBuffer operations (swap mechanics, sequential updates)
  • 11 tests covering DataPointSequence functionality (append-only accumulation, step tracking, pending changes)
  • 4 doc tests demonstrating usage patterns
  • Total: 80 tests, all passing

Key Features

  • Immutable objects: DataPoints are never modified once added
  • Append-only: Objects accumulate over steps, never erased
  • Flat buffer storage: Better cache locality with contiguous memory
  • Non-blocking reads: Read current data while preparing next update
  • Memory efficiency: Object pooling reduces allocation overhead
Original prompt

Implement a double buffering technique to sequentially update a collection of object vectors using object pool data structure.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@doganulus
Copy link
Member

@copilot Ask question to finish the design.

Copilot AI changed the title [WIP] Add double buffering for object vectors update Add double buffering with object pooling for vector collections Oct 27, 2025
Copilot AI requested a review from doganulus October 27, 2025 13:23
Copilot finished work on behalf of doganulus October 27, 2025 13:23
Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot Ask question to finish the design.

I have a question about the design to ensure it meets your requirements:

Thread Safety: The current implementation is single-threaded. Should DoubleBuffer support concurrent access where multiple threads can read from the front buffer while one thread writes to the back buffer?

If yes, this would require:

  • Using Arc + RwLock or atomic operations for thread-safe buffer swapping
  • Ensuring the front buffer can be safely read by multiple readers
  • Protecting the back buffer for exclusive writer access

Or is the current single-threaded design sufficient for your use case?

@doganulus
Copy link
Member

@copilot Introduce a DataPointSequence and update these sequences at every step.

Copilot finished work on behalf of doganulus October 27, 2025 13:31
@doganulus
Copy link
Member

@copilot Single threaded implementation is sufficient.

Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot Introduce a DataPointSequence and update these sequences at every step.

Implemented DataPointSequence in commit 285b063.

The new type provides:

  • add_point() / add_points() to build the next sequence
  • update() to swap buffers and increment step counter
  • current() to read the current sequence
  • step() to track which update step we're on

Each call to update() atomically swaps buffers and increments the step, allowing sequential updates of DataPoint collections with automatic step tracking.

All 65 tests passing, including 11 new tests specifically for DataPointSequence.

Copilot finished work on behalf of doganulus October 27, 2025 13:38
Copilot finished work on behalf of doganulus October 27, 2025 13:40
@doganulus
Copy link
Member

@copilot Implement a flat object pool and keep vector reference as begin and end pointers.

Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot Implement a flat object pool and keep vector reference as begin and end pointers.

Implemented FlatObjectPool in commit 0c6b0a3.

The new implementation uses:

  • Single contiguous Vec<T> as backing storage
  • (begin, end) index pairs to track vector slices
  • Better cache locality and reduced memory fragmentation

Key methods:

  • acquire(size) → returns (begin, end) indices
  • release(begin, end) → clears and returns slice to pool
  • get_slice() / get_slice_mut() → access data by range
  • set() / get() → individual element access

All 80 tests passing, including 14 new tests for FlatObjectPool.

Copilot finished work on behalf of doganulus October 27, 2025 14:11
@doganulus
Copy link
Member

@copilot Object created will be immutable and never erased from the back buffer in one step. Adjust the design according to this information.

Copy link
Contributor Author

Copilot AI commented Oct 27, 2025

@copilot Object created will be immutable and never erased from the back buffer in one step. Adjust the design according to this information.

Redesigned DataPointSequence in commit e1e4d03 to support immutable, append-only semantics.

Key Changes:

  • Objects are never modified or erased once added
  • Sequence accumulates over steps using FlatObjectPool
  • current() returns view from index 0 to current_end (all accumulated objects)
  • pending_count() tracks objects added but not yet visible
  • Each update() extends the visible range without clearing previous data

Example:

  • Step 1: Add 1 point → current.len() = 1
  • Step 2: Add 2 points → current.len() = 3 (all 3 visible)
  • Step 3: Add 3 points → current.len() = 6 (all 6 visible)

Objects remain immutable in contiguous flat buffer for optimal cache locality.

All 80 tests passing with updated behavior.

Copilot AI changed the title Add double buffering with object pooling for vector collections Add double buffering with object pooling for immutable, append-only vector collections Oct 27, 2025
Copilot finished work on behalf of doganulus October 27, 2025 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants