enso-channel
Bounded. Lock-free. Batch-native.
A batch-first concurrency primitive for bursty, latency-sensitive systems.
Table of Contents
- What is enso-channel?
- Design Philosophy
- Non-Goals
- Communication Patterns
- System-Wide Tunability
- Getting Started
- Disconnection (RAII)
- Performance Characteristics
- Benchmark Overview
- Correctness & Testing
- When to Use enso-channel
- Roadmap
- License
What is enso-channel?
enso-channel explores a batch-first design space for lock-free ring-buffer channels in Rust.
Built for systems where
- Burst traffic is common
- Tail latency (p99 / p99.9) matters
- Backpressure must be explicit
- Memory is bounded and pre-allocated
- Scheduling is handled at a higher layer
Note: If you need async/await integration, blocking APIs, or dynamic resizing — this crate is probably not for you.
Design Philosophy
enso-channel is a concurrency primitive, not a runtime.
Core Principles
- Lock-free
- Bounded capacity
- Pre-allocated memory
- Explicit backpressure
- Native batch claim/commit
- No hidden scheduling
- No implicit blocking
Mental Model
Instead of sending items one-by-one:
1. Claim a contiguous range in the ring buffer
2. Write into it / Read from it
3. Commit the range
Batching amortizes synchronization cost and reduces tail latency under burst workloads.
Inspired by
But with
- Rust-native channel ergonomics
- Unified API across topologies
- Clear primitive boundaries
Non-Goals
To prevent misuse and ambiguity:
- No blocking API
- No async integration
- No dynamic resizing
- No built-in scheduling policy
- No priority queues
- No fairness guarantees beyond lock-free progress
Philosophy: Scheduling, wake-up strategy, budgeting, priority, and flow control belong to higher layers.
enso-channel intentionally surfaces Full, Empty, and Disconnected instead of hiding them behind blocking semantics.
Communication Patterns
Unified API across multiple topologies:
| Pattern | Description |
|---|---|
| SPSC | Single Producer, Single Consumer |
| MPSC | Multiple Producers, Single Consumer |
| Broadcast | Lossless Broadcast (fixed‑N fanout) |
| MPMC | Work Distribution |
Key Properties
- Receivers may initiate disconnect
- MPMC receivers can be cloned
- Broadcast topology is fixed at creation
- Disconnection follows RAII semantics
- All patterns support batch operations
System-Wide Tunability
Tunability in enso-channel is not limited to a specific topology.
It emerges from its batch-native design.
Every pattern supports
| API | Description |
|---|---|
try_send_many / try_recv_many |
Batch claiming |
try_send_at_most / try_recv_at_most |
At-most semantics |
| Explicit backpressure | Surface Full / Empty |
This enables systematic control over
- Contention levels
- Cache locality
- Burst amortization
- Producer/consumer balance
- Tail latency behavior
Note: Under high contention (e.g. MPMC), the effect is especially visible — but tunability is inherent to the design, not limited to one pattern.
Getting Started
Installation
Add to your project:
Example (MPSC)
use mpsc;
All topologies share a consistent API shape.
See documentation for additional examples.
Disconnection (RAII)
enso-channel does not expose an explicit close() API.
Disconnection follows RAII
- Dropping the last sender disconnects receivers (after draining committed items)
- Dropping the last receiver disconnects senders
- Batch guards commit automatically on drop
Important Caveat
Disconnection is eventual, not transactional.
A sender may still successfully publish if a receiver is dropped concurrently. Already-committed items may never be observed by the application.
Graceful shutdown without loss
1. Stop producing
2. Drop all senders
3. Drain on the receiver until `Disconnected`
Performance Characteristics
Optimized for
- Burst latency
- Tail behavior (p99 / p99.9)
- Busy-spin workloads
- Deterministic bounded pipelines
Not optimized for
- Blocking workloads
- Async runtimes
- Unbounded queues
- General-purpose task scheduling
Benchmark Overview
Benchmarks focus on
- ns per burst
- Mean and p99.9
- Busy-spin
try_*loops - Pinned threads (configurable)
- Fixed buffer sizes
These measurements reflect burst-latency workloads under contention. They are not intended as general-purpose channel comparisons.
The numbers below are a selected snapshot to illustrate burst-latency behavior.
Notes
- All benches in this section report ns/burst (lower is better)
- The results are from a specific run on a MacBook Pro M4 (24G); your mileage may vary
broadcastmeasures a burst as complete when it is delivered to all consumers- These benches pin threads by default (set
ENSO_CHANNEL_PINNING=offto disable) - Outputs are printed to stdout and also written as CSV + table to a local output dir (default:
benches/results, generated and gitignored). Override withENSO_SPSC_OUTPUT_DIR/ENSO_MPSC_OUTPUT_DIR/ENSO_MPMC_OUTPUT_DIR/ENSO_BROADCAST_OUTPUT_DIR
To reproduce
For additional workloads/topologies, run the other bench targets under benches/.
SPSC (ns/burst)
| burst | enso mean | enso p99.9 | crossbeam mean | crossbeam p99.9 |
|---|---|---|---|---|
| 1 | 193.6 | 875 | 280.2 | 834 |
| 16 | 250.1 | 792 | 578.8 | 2835 |
| 64 | 357.0 | 792 | 1157.7 | 9047 |
| 128 | 495.4 | 1625 | 2271.0 | 17967 |
MPSC (ns/burst)
2 producers
| burst | enso mean | enso p99.9 | crossbeam mean | crossbeam p99.9 |
|---|---|---|---|---|
| 1 | 673.1 | 2209 | 1022.6 | 7711 |
| 16 | 988.3 | 7919 | 2735.3 | 15007 |
| 64 | 1161.2 | 7875 | 4514.5 | 19087 |
| 128 | 1333.4 | 12839 | 6518.1 | 36319 |
4 producers
| burst | enso mean | enso p99.9 | crossbeam mean | crossbeam p99.9 |
|---|---|---|---|---|
| 1 | 1272.5 | 10711 | 1274.1 | 9215 |
| 16 | 1273.0 | 9007 | 7650.7 | 28431 |
| 64 | 1690.1 | 11711 | 21274.8 | 49919 |
| 128 | 2305.5 | 16295 | 35566.1 | 81663 |
Work queue / MPMC tunability (ns/burst)
This bench measures work distribution (MPMC): total messages per burst = burst * producers,
and the burst is complete when the set is consumed by the worker pool.
For enso_channel::mpmc, we report the best try_send_at_most / try_recv_at_most tuning
by lowest p99.9 among the tested (send_limit, recv_limit) pairs.
P2C2
| burst | enso tuning (send_limit, recv_limit) | enso mean | enso p99.9 | crossbeam mean | crossbeam p99.9 |
|---|---|---|---|---|---|
| 1 | (8, 64) | 1396.8 | 3001 | 1373.5 | 3251 |
| 16 | (8, 32) | 1553.1 | 2501 | 3502.0 | 9631 |
| 32 | (8, 32) | 1866.0 | 3833 | 4228.3 | 13959 |
| 64 | (8, 32) | 2887.7 | 6375 | 6074.2 | 19295 |
P4C4
| burst | enso tuning (send_limit, recv_limit) | enso mean | enso p99.9 | crossbeam mean | crossbeam p99.9 |
|---|---|---|---|---|---|
| 1 | (4, 32) | 1835.6 | 10295 | 2037.1 | 10591 |
| 16 | (8, 64) | 2453.8 | 10215 | 11610.8 | 127679 |
| 32 | (8, 32) | 4171.4 | 12463 | 15574.9 | 360703 |
| 64 | (8, 64) | 7390.4 | 15503 | 20208.5 | 598527 |
Broadcast / SPMC scalability (ns/burst)
This bench reports ns/burst: one producer publishes a burst, and the burst is complete when all consumers have received it.
| burst | C=2 mean | C=2 p99.9 | C=4 mean | C=4 p99.9 |
|---|---|---|---|---|
| 1 | 418.9 | 3417 | 751.1 | 1334 |
| 16 | 379.6 | 1334 | 756.7 | 4251 |
| 64 | 431.2 | 1334 | 723.3 | 1500 |
| 128 | 414.9 | 1417 | 817.1 | 1708 |
Correctness & Testing
- Miri clean
- Tested on ARM (Apple Silicon) and x86_64
- Memory ordering documented in source
- Loom integration planned / in progress
Contracts (panic + RAII guards)
enso-channel is a lock-free primitive optimized for fast-path performance.
It intentionally does not impose a heavyweight orchestration layer (parking/blocking/spinning,
panic recovery, strict state reconciliation) on every operation.
As a result, some behaviors are caller contracts:
- Send batch factories must not panic.
try_send_many*/try_send_at_most*use a factory to fill any unwritten slots when a send batch is finished or dropped. If that factory panics (including during drop), the claimed range may remain uncommitted and can wedge progress or permanently reduce capacity. - Receive guards commit on drop. Dropping a receive batch commits the full claimed range. If you drop it without iterating, unread items are skipped (considered consumed).
If you need stronger “panic containment” or recovery behavior, build it on top of this primitive in your orchestration layer.
Lock-free correctness and memory ordering are treated as first-class concerns.
When to Use enso-channel
Use it if
- You control your scheduling model
- You prefer explicit backpressure
- Your workload is burst-heavy
- Tail latency matters
- You want deterministic bounded behavior
Avoid it if
- You need async/await integration
- You want blocking
recv() - You prefer runtime-managed scheduling
- You require dynamic capacity growth
Roadmap
- Loom verification
- Additional workload benchmarks
- More usage examples
- Documentation expansion
Note: No plans for async or blocking APIs.
License
Licensed under either:
- Apache License, Version 2.0
- MIT License