swactor/CLAUDE/notes/progress.md
Developer e91308720b feat: identity hashing for ActorAddress hot-path optimization (Cycle 20)
Replace SipHash with identity hasher on all hot-path HashMaps keyed by
ActorAddress. Since addresses are crypto-random, the first 8 bytes serve
as an excellent hash directly. Microbenchmarks show 1.9-4.6x lookup
speedup depending on map size.

- Custom Hash impl for ActorAddress (8-byte write_u64 instead of 32)
- AddrHasher/AddrBuildHasher identity hasher in delivery.rs
- AddrMap<V>/AddrSet type aliases used in 7 HashMap sites
- Stop-requests is_empty() short-circuit in tick_all inner loop
- 3 new behavioral tests, component microbenchmarks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:01:30 +00:00

3.7 KiB

Progress Log

Current Stage: Cycle 20 — Hot-Path Performance (Identity Hashing)

Status: COMPLETE

Research

  • Stakker: Closure-based dispatch. call! macro generates FnOnce closures pushed to a flat byte Vec queue. No HashMap, no Box, no downcast. Direct method calls that the compiler can inline. Single-threaded only.
  • Actix: Vtable dispatch via Box<dyn EnvelopeProxy<A>>. Addr<A> is a direct channel reference (no HashMap lookup). Custom Vyukov lock-free MPSC queue. Default mailbox capacity 16.
  • Key insight: Both avoid HashMap entirely by using direct references. Swactor needs HashMaps for location-transparent 32-byte addresses (multi-worker routing + transport). The optimization is to minimize HashMap cost, not eliminate it.
  • Full analysis: CLAUDE/notes/dispatch_comparison.md

Implementation

  1. Custom Hash for ActorAddress (src/actor.rs) — only hashes first 8 bytes instead of 32. All HashMaps using ActorAddress benefit automatically.
  2. Identity hasher (src/delivery.rs) — AddrHasher/AddrBuildHasher that passes the 8-byte hash value through as the bucket index directly, skipping SipHash.
  3. Hot-path HashMap replacement — AddrMap<V> type alias used in:
    • AddressMap.inner (delivery.rs) — on every send
    • ActorPool.actors (worker.rs) — on every deliver and tick_all
    • InboxRegistry.senders (delivery.rs)
    • MonitorRegistry.monitors (delivery.rs)
    • NameRegistry.reverse (delivery.rs)
    • GroupRegistry.memberships + AddrSet for group member sets (delivery.rs)
    • TransportRouter.routes (transport.rs)
  4. Stop-requests optimization (worker.rs) — is_empty() short-circuit before linear scan in inner message loop

Microbenchmark Results (reliable, same-process A/B)

Operation SipHash Identity Speedup
Hash 1.35 ns 0.67 ns 2.0x
Lookup/100 29.8 ns 15.5 ns 1.9x
Lookup/1000 38.0 ns 8.2 ns 4.6x
Insert 1000 48.2 µs 21.1 µs 2.3x

Note: End-to-end benchmarks unreliable in sandbox (55% variation between identical runs). Microbenchmarks confirmed significant hash/lookup improvement.

Tests

  • 143 behavioral tests pass (140 existing + 3 new)
  • 7 proptest pass
  • New tests:
    • many_actors_all_receive_correct_messages — 200 actors, verifies no misrouting from identity hasher
    • ring_routing_unchanged_after_hasher_optimization — 100-actor chain, verifies address_map correctness
    • stop_self_with_pending_messages_still_works — verifies stop_requests optimization correctness

Files Modified

  • src/actor.rs — Custom Hash impl for ActorAddress (8-byte)
  • src/delivery.rs — AddrHasher, AddrBuildHasher, AddrMap, AddrSet types; 5 HashMap replacements
  • src/worker.rs — ActorPool.actors AddrMap; stop_requests optimization
  • src/transport.rs — TransportRouter.routes AddrMap
  • Cargo.toml — Added hasher_benchmarks bench entry
  • benches/hasher_benchmarks.rs — New: component-level microbenchmarks
  • tests/runtime_api.rs — 3 new behavioral tests
  • CLAUDE/notes/dispatch_comparison.md — New: Stakker/Actix/Swactor analysis

Next Steps

  • Profile the Box::new(msg) allocation cost — SmallBox/inline storage could eliminate heap alloc for small messages
  • Investigate VecDeque mailbox alternative (slab-allocated ring buffer)
  • Consider enum_dispatch pattern for avoiding dyn Any downcast (would require API changes)
  • Benchmark on dedicated hardware (sandbox too noisy for reliable end-to-end measurement)

Open Questions

  • Is 8 bytes sufficient for the identity hash? (Yes — 2^64 from crypto-random bytes)
  • Should we provide a with_hasher public API for users? (No — internal optimization only)