He-Pin opened a new pull request, #3091: URL: https://github.com/apache/pekko/pull/3091
### Motivation `Source.actorRef` used `getEagerStageActor` which creates a `(ActorRef, Any)` Tuple2 (~24 bytes) per message via the StageActor FunctionRef lambda, even though the sender is never used. Additionally, when the buffer is empty and downstream has demand, elements went through an unnecessary enqueue→dequeue round-trip. A separate correctness issue existed where `StageActor.localCell` could throw `IllegalStateException` on the first materialization of a fresh ActorSystem due to an `UnstartedCell` race. Inspired by [akka.net#8263](https://github.com/akkadotnet/akka.net/pull/8263) which achieved -35% latency / +53% throughput on the same code path. ### Modification 1. **Fix UnstartedCell race** (`GraphStage.scala`): `StageActor.localCell` now handles `UnstartedCell` by force-starting the supervisor via `Ask(Identify)`. This is a global fix — all stages using `getStageActor`/`getEagerStageActor` benefit. 2. **Direct-push fast path** (`ActorRefSource.scala`): When the buffer is empty and downstream has demand, elements are pushed directly without going through the buffer, eliminating the enqueue→dequeue overhead. 3. **Bypass StageActor** (`ActorRefSource.scala`): Replace `getEagerStageActor` with a direct `FunctionRef` on the supervisor's `ActorCell`, using a message-only `AsyncCallback[Any]` — no tuple boxing. PoisonPill/Kill are intercepted at the FunctionRef level. 4. **Shared cell resolution**: Extracted `resolveSupervisorCell` on `GraphStageLogic` to eliminate duplicated cell resolution logic. 5. **JMH benchmarks**: Added `ActorRefSourceBenchmark` with two targeted measurements: - `no_buffer` (buffer=0): isolates FunctionRef+AsyncCallback path cost - `pingpong` (buffer=1, sync send-wait): 100% direct-path hits ### Result Benchmark results (10 iterations, 5 warmup, same machine): | Benchmark | Before | After | Δ | |-----------|-------:|------:|---:| | no_buffer (buffer=0) | 18.0M ±0.5M | **19.5M ±1.0M** | **+8.3%** | | pingpong (buffer=1, sync) | 883K ±34K | **988K ±29K** | **+11.9%** | Both benchmarks show statistically significant improvement (99.9% CI non-overlapping). Per-element allocation reduced by ~24 bytes (Tuple2 elimination). ### Tests - `ActorRefSourceSpec`: 18/18 (incl. 5 new regression tests) - UnstartedCell startup race - Direct-push fast path - PoisonPill ignored for T=Any - Kill ignored for T=Any - Post-completion message routing - `StageActorRefSpec`: 11/11 (no regressions) - `ActorRefBackpressureSourceSpec`: 5/5 (no regressions) ### References Refs https://github.com/akkadotnet/akka.net/pull/8263 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
