-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Performance Audit
Analysis of the 0.5.0 refactor (#[protocol] + #[actor] macros, Handler<M>, envelope pattern) compared to the 0.4.x enum-based approach. These are optimization opportunities — none are blockers for 0.5.0.
High Impact
1. Threads mode: 100ms polling loop for cancellation — #157
threads/actor.rs uses rx.recv_timeout(Duration::from_millis(100)) to periodically check cancellation_token.is_cancelled(). This causes:
- Up to 100ms shutdown latency
- Constant wakeups even when idle (10/sec)
- CPU waste on idle actors
Fix: Poison-pill approach — send a sentinel through the mailbox channel on stop(), replacing the timeout loop with blocking rx.recv().
Medium Impact
2. Two heap allocations per message (tasks mode)
Each message incurs:
Box<dyn Envelope<A>>for type erasure into the mailboxPin<Box<dyn Future>>fromEnvelope::handle()returning a boxed future
The old 0.4.x code used ActorInMsg<A> enum with associated types — zero extra heap allocs. The new design trades this for flexibility (unlimited message types via Handler<M>).
Possible mitigations: SmallBox for stack-allocating small envelopes, arena/bump allocators for batch allocation, monomorphization for hot paths.
3. Box<dyn Envelope> per message (threads mode)
Same envelope boxing as tasks, but no future boxing (sync handlers). Still one heap alloc per message that didn't exist in 0.4.x.
4. No message batching
The run loop processes one message at a time, checking cancellation between each. Draining multiple messages before yielding could reduce overhead under high throughput. Marginal for tasks mode (tokio batches internally), more impactful for threads.
Low Impact
5. CancellationToken uses SeqCst atomics
rt/src/threads/mod.rs uses AtomicBool with Ordering::SeqCst. Acquire/Release would suffice and is slightly cheaper on ARM. Negligible on x86.
6. Extra mpsc channel per timer (threads mode)
Each send_after/send_interval creates a dedicated mpsc::channel() for cancellation. Could reuse AtomicBool instead.
7. Unbounded mailbox — no backpressure
Both modes use unbounded channels. Same as 0.4.x and Erlang, but bounded channels with configurable capacity could be a future option.
Priority
- 0.5.1: Item 1 (threads polling loop) — perf: replace threads 100ms polling loop with poison-pill shutdown #157
- Future: Items 2-4 (envelope/future allocation — profile first)
- Nice to have: Items 5-7