Skip to content

messages sent to actor are not boxed anymore#370

Open
Kannen wants to merge 10 commits intoslawlor:mainfrom
Kannen:better-actor-cell
Open

messages sent to actor are not boxed anymore#370
Kannen wants to merge 10 commits intoslawlor:mainfrom
Kannen:better-actor-cell

Conversation

@Kannen
Copy link
Copy Markdown
Contributor

@Kannen Kannen commented Jul 18, 2025

While navigating inside the code I realized that every message sent to local actor are boxed.

So I made a set of simple changes so that message sent to actor are directly sent to the mpsc channel without being boxed. This avoid one allocation per message.

The benchmarks show improvements. Moreover I think a more realistic benchmark would show much more improvement as avoiding the allocation should enhance cache locality during real code execution.

In this pull request I have used unsafe code, as it avoid to do a redundant type_id check, but it can implemented with 100% with safe code, at the cost of a call to Any::downcast_ref at each message sent.

@Kannen
Copy link
Copy Markdown
Contributor Author

Kannen commented Jul 21, 2025

I have written some benchmarks, it improve OutputPort message dispatching by 50% and concurrent processing and message sending by 22%.

@Kannen
Copy link
Copy Markdown
Contributor Author

Kannen commented Jul 22, 2025

Just to show you how interesting in those changes I propose:

  • all messages sent to output ports are delivered
  • removal of some race conditions inside pg

And speed enhancement, this is the comparison of the main branch with the fusion of the changes I propose:

     Running benches/actor.rs (target/release/deps/actor-8267de2bad91941e)
Creation of 100 actors  time:   [621.28 µs 623.51 µs 625.64 µs]
                        change: [-5.9816% -5.5918% -5.2311%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) low mild

Benchmarking Creation of 10000 actors: Warming up for 3.0000 s
Warning: Unable to complete 100 samples in 5.0s. You may wish to increase target time to 8.0s, or reduce sample count to 60.
Creation of 10000 actors
                        time:   [71.562 ms 71.927 ms 72.288 ms]
                        change: [-4.3784% -3.7695% -3.1403%] (p = 0.00 < 0.05)
                        Performance has improved.

Waiting on 100 actors to process first message
                        time:   [28.116 µs 28.207 µs 28.308 µs]
                        change: [-16.420% -12.757% -9.7980%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 10 outliers among 100 measurements (10.00%)
  2 (2.00%) low mild
  4 (4.00%) high mild
  4 (4.00%) high severe

Waiting on 1000 actors to process first message
                        time:   [212.16 µs 213.01 µs 213.91 µs]
                        change: [-3.6355% -1.9731% -0.7051%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 6 outliers among 100 measurements (6.00%)
  6 (6.00%) high mild

Waiting on 100000 messages to be processed
                        time:   [29.750 ms 29.920 ms 30.114 ms]
                        change: [-18.567% -17.969% -17.292%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) high mild
  1 (1.00%) high severe

Benchmarking Waiting on 100000 messages to be sent and processed concurrently: Collecting 100 samples in estimated 5.4903 s (200 itWaiting on 100000 messages to be sent and processed concurrently
                        time:   [27.199 ms 27.341 ms 27.493 ms]
                        change: [-26.341% -25.865% -25.322%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 12 outliers among 100 measurements (12.00%)
  11 (11.00%) high mild
  1 (1.00%) high severe

Benchmarking Waiting on 1000 messages to be sent on output port to 100 actors: Collecting 100 samples in estimated 5.2824 s (500 itWaiting on 1000 messages to be sent on output port to 100 actors
                        time:   [10.123 ms 10.139 ms 10.155 ms]
                        change: [+0.6423% +1.3110% +1.8904%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 6 outliers among 100 measurements (6.00%)
  1 (1.00%) low mild
  4 (4.00%) high mild
  1 (1.00%) high severe

     Running benches/async_traits.rs (target/release/deps/async_traits-4e1c090e51e5824e)
Benchmarking Waiting on 50 messages with large data in the Future to be processed: Collecting 100 samples in estimated 5.6369 s (20Waiting on 50 messages with large data in the Future to be processed
                        time:   [92.790 µs 94.889 µs 96.931 µs]
                        change: [-14.356% -12.087% -9.8323%] (p = 0.00 < 0.05)
                        Performance has improved.

     Running unittests src/lib.rs (target/release/deps/ractor_cluster-5a0dcb660cf38e6f)

@codecov
Copy link
Copy Markdown

codecov Bot commented Jul 23, 2025

Codecov Report

❌ Patch coverage is 97.12230% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.62%. Comparing base (a5e0c65) to head (b94ee94).
⚠️ Report is 23 commits behind head on main.

Files with missing lines Patch % Lines
ractor/src/message.rs 85.71% 2 Missing ⚠️
ractor/src/actor/actor_properties.rs 98.96% 1 Missing ⚠️
ractor/src/thread_local/inner.rs 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #370      +/-   ##
==========================================
+ Coverage   82.48%   82.62%   +0.14%     
==========================================
  Files          71       71              
  Lines       12919    12984      +65     
==========================================
+ Hits        10656    10728      +72     
+ Misses       2263     2256       -7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@slawlor slawlor force-pushed the better-actor-cell branch from d26a55b to 69ef01a Compare July 23, 2025 17:43
@Kannen
Copy link
Copy Markdown
Contributor Author

Kannen commented Aug 11, 2025

The difference shown in code coverage are due to the fact I have move around some piece of code from one file to the other. This does not change the number of branches tested.
The lacking test all relates to the "cluster" feature and I do not know yet how to use this feature and how even less how to test it.

Kannen added 4 commits August 13, 2025 18:13
Those function may only be called in extraordinary conditions: the
actor must be killed during the call of send_message.
}

#[allow(elided_named_lifetimes)]
#[allow(mismatched_lifetime_syntaxes)]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rebase and this should go away

Comment thread ractor/src/message.rs
/// constraints
pub struct BoxedMessage {
pub(crate) msg: Option<Box<dyn Any + Send>>,
pub struct BoxedMessage<T: Any + Send> {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this is a semver break anyways, we'll need a major release to do this, and should just get rid of BoxedMessage.

It's a poor name given the message would no longer be boxed in this case.

Message(BoxedMessage),
Message(BoxedMessage<T>),
}
pub(crate) trait GenericInputPort: Sync + Send + Any + 'static {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this need to be a trait? Is this just for ergonomics? These seem like blanket impl helper functions, I don't see the need for this here

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind looking more closely at the PR, I see the reasoning.

slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
@slawlor
Copy link
Copy Markdown
Owner

slawlor commented Aug 20, 2025

@Kannen I can't reproduce your savings (outside maybe of output ports), but I'm dubious if this saves much on processing speed of messages in reality. Have you run the benchmarks multiple times to avoid jitter? You need to be doing it on a quiet host too so there's limited contention.

See my results in #387, which shows there's little difference in the regular suite of tests.

slawlor added a commit that referenced this pull request Aug 20, 2025
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 9, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 9, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 9, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 9, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 10, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 10, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 10, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 10, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 10, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
slawlor added a commit that referenced this pull request Mar 11, 2026
This is an extension of the message boxing approach proposed in PR #370. This however continues the improvements to remove additional type information that isn't necessary.

This diff includes the removal of the BoxedMessage struct in favor of a crate-private RactorMessage trait which adds the necessary extension methods, only within the `ractor`
context so as the privatize that functionality, as it was an unintentional leak anyways. This of course is a semver break as a public type is going away, but it should have been unused
anyways in all contexts. Additionally `BoxedDowncastErr` is renamed to `DowncastErr` as we are no longer boxing the message, but again this error variant shouldn't really be used publicly.

Will re-run the performance benchmarks present in #370 and past as a PR comment once ready
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants