Skip to content

perf: performance audit findings for 0.5.0+ #156

@ElFantasma

Description

@ElFantasma

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 mailbox
  • Pin<Box<dyn Future>> from Envelope::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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions