BitWheel
A high-performance hierarchical timer wheel for Rust, optimized for low-latency trading systems and event-driven applications.
Features
- O(1) insert, cancel, and poll — constant-time operations regardless of timer count
- Zero allocation on hot path — all memory pre-allocated at construction
- Nanosecond-level latency — p50 poll ~15ns, p50 insert ~25ns
- Hierarchical design — efficiently handles timers from milliseconds to hours
- Skip-ahead optimization — jumps directly to next fire time, no idle iteration
- Configurable via const generics — tune gears, resolution, capacity at compile time
Performance
Measured on realistic trading workload (order timeouts + cancellations):
| Operation | p50 | p99 | p99.9 |
|---|---|---|---|
| Poll | 15ns | 171ns | 224ns |
| Insert | 25ns | 41ns | 99ns |
| Cancel | 14ns | 30ns | 59ns |
Installation
[]
= "0.1"
Usage
Define a Timer
use ;
use ;
Basic Operations
// Create a timer wheel (boxed due to size)
let mut wheel: = boxed;
// Insert a timer
let when = now + from_millis;
let handle = wheel.insert?;
// Cancel if needed (e.g., order filled)
let timer = wheel.cancel;
// Poll in your event loop
let mut fired_orders = Vecnew;
let num_fired = wheel.poll?;
Periodic Timers
Custom Configuration
use BitWheel;
// BitWheel<Timer, NUM_GEARS, RESOLUTION_MS, SLOT_CAP, MAX_PROBES>
type FastWheel<T> = ;
let mut wheel: = boxed;
| Parameter | Default | Description |
|---|---|---|
NUM_GEARS |
5 | Hierarchy levels (more = longer max delay) |
RESOLUTION_MS |
5 | Tick resolution in milliseconds |
SLOT_CAP |
32 | Max timers per slot before probing |
MAX_PROBES |
3 | Linear probe limit on collision |
Delay Coverage by Gears
| Gears | Max Delay | Use Case |
|---|---|---|
| 4 | ~4.6 hours | Intraday trading |
| 5 | ~12 days | Multi-day sessions |
| 6 | ~2.2 years | Long-dated expirations |
Event Loop Integration
use ;
Architecture
BitWheel uses a hierarchical timer wheel with 64 slots per gear:
Gear 0: ticks 1-63 (finest resolution)
Gear 1: ticks 64-4095 (64x coarser)
Gear 2: ticks 4096-262143 (4096x coarser)
...
Each slot contains a fixed-capacity slab with O(1) insert/remove via intrusive linked lists. Collisions are handled by linear probing to adjacent slots.
Key optimizations:
- Skip-ahead: Jumps directly to
next_fire_tick, avoiding idle iteration - Dirty tracking: Only recomputes gear metadata when timers are removed
- Bitmap operations: O(1) next-slot lookup via
rotate_right+trailing_zeros