-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
refactor: dynamic dispatch async commands #13464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: dynamic dispatch async commands #13464
Conversation
Package Changes Through 9ff6c70There are 8 changes which include tauri-bundler with minor, tauri with minor, tauri-cli with minor, tauri-codegen with minor, tauri-utils with minor, @tauri-apps/api with minor, @tauri-apps/cli with minor, tauri-runtime-wry with patch Planned Package VersionsThe following package releases are the planned based on the context of changes in this pull request.
Add another change file through the GitHub UI by following this link. Read about change files or the docs at github.com/jbolda/covector |
| pub fn respond_async_serialized<F>(self, task: F) | ||
| where | ||
| F: Future<Output = Result<InvokeResponseBody, InvokeError>> + Send + 'static, | ||
| { | ||
| pub fn respond_async_serialized( | ||
| self, | ||
| task: Pin<Box<dyn Future<Output = Result<InvokeResponseBody, InvokeError>> + Send + 'static>>, | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that respond_async_serialized is a pub API, isn't this a BREAKING CHANGE?
maybe we could enable this for the debug build and still use the old static dispatch one for the release build?
In my opinion, having different signatures in debug and release modes would be confusing.
Not sure if I am correctly: if we only pass in Box::pin(...) as Pin<Box<dyn ...>>, then only a single template instantiation of respond_async_serialized<Pin<Box<dyn ...>>> will occur, which is effectively the same as changing the signature to Pin<Box<dyn ...>>, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that respond_async_serialized is a pub API, isn't this a BREAKING CHANGE?
I believe this part was marked as unstable?
In my opinion, having different signatures in debug and release modes would be confusing.
That's true though, I feel like the performance hit probably won't be a problem considering this is only a pretty small part of the call chain anyways
Not sure if I am correctly: if we only pass in Box::pin(...) type, then only a single template instantiation of respond_async_serialized will occur, which is effectively the same as changing the signature to Pin<Box<dyn ...>>, right?
I believe the box itself will create a new generic variation for every closure, so this probably won't work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not entirely sure if this was marked as unstable or not, will double check that tomorrow, but since we manage it through macros, it should be easy to add a new function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's probably meant to be unstable but we only communicated that on the "parent" Invoke struct.
honestly though? in my opinion marking anything pub in our main tauri crate as unstable only via a doc comment is bullshit. Ideally unstable APIs should not be public, or clearly unstable (feature flag, or in an unstable module path idk) but that's a discussion for later i guess.
Since we're working on v3 i think marking it as deprecated would be the more sensible choice here.
"working" as in praying it will somehow appear out of nowhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am already directly using Invoke downstream (in pytauri), so I always hope these APIs change as little as possible (v3) 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's probably meant to be unstable but we only communicated that on the "parent" Invoke struct.
Yeah, I think so after checking
honestly though? in my opinion marking anything pub in our main tauri crate as unstable only via a doc comment is bullshit. Ideally unstable APIs should not be public, or clearly unstable (feature flag, or in an unstable module path idk) but that's a discussion for later i guess.
To be fair though, some of the things like generate_handler! and generate_context! and the related code are impossible to be stable and they still have to be public
Normally, users won't use them directly so it's fine to break, but it's probably a problem for the library authors (I pinned the tauri version in tauri-runtime-verso, and also we can see the impact on pytauri as well)
I am already directly using Invoke downstream (in pytauri), so I always hope these APIs change as little as possible (v3) 😂
I have tried to make a inner private function so the public API won't change from this, and it seems to only produce slightly more LLVM IR than doing it from the macro, pushed it
Thanks for the heads up @WSH032 about avoiding this breaking change!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the question is now, do we want to use this for both the debug build and the release build, or do we only do this in debug build for now, since this is done with a private inner function, we should be able to change this without any breakages
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested this Box::pin(...) as Pin<Box<dyn ...>>, and from the assembly output it does seem that only one instantiation of respond_async_serialized<Pin<Box<dyn ...>>> occurs: https://godbolt.org/z/9sMMqdo1o
To be fair though, some things like
generate_handler!andgenerate_context!and the related code are impossible to make stable, and they still have to be public
I think I'll use #[doc(hide)] to hide them.
And the question is now, do we want to use this for both the debug build and the release build, or do we only do this in debug build for now
I think everyone has their own preferences. Personally, I don't care about code bloat, so I would prefer using dyn in debug mode to speed up development, and using impl generics in release mode to optimize performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested this Box::pin(...) as Pin<Box<dyn ...>>, and from the assembly output it does seem that only one instantiation of respond_async_serialized<Pin<Box<dyn ...>>> occurs: https://godbolt.org/z/9sMMqdo1o
Interesting, we could try that as well (I do want to avoid thing inside the macro in general though, so if the inner function method works, I might prefer that one)
I think I'll use #[doc(hide)] to hide them.
We can't hide the top level macros like generate_handler!, but we probably can hide the other ones, but to be fair, they still show up in IDEs so I'm not sure how effective would this be
I think everyone has their own preferences. Personally, I don't care about code bloat, so I would prefer using dyn in debug mode to speed up development, and using impl generics in release mode to optimize performance.
Good to know, I feel like this probably won't be a problem in terms of performance since the overhead of other things will heavily out weight the cost of this, but I'm fine with keeping it the same in release mode for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be fair though, some of the things like generate_handler! and generate_context! and the related code are impossible to be stable and they still have to be public
Well there are exceptions to everything, it just shouldn't be the standard.
FabianLars
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
didn't test this yet. if you want me too i can do so by tomorrow. looks like a safe merge tho
|
I'll merge this for now since I'm preparing a follow up PR, would be nice to have some comparison data on mac and linux though 🙃 |
|
I'll try to remember that lol |
|
@Legend-Master My very professional testing on macos showed a decrease from 7secs to ~3.2-3.3secs so pretty similar to your result (40% vs 45%) |
|
Nice! Thanks for testing, incremental build is the most important factor to me, this should help a lot 😀 |
* Dynamic dispatch async commands * format * Preserve `'static` * Use a inner function instead * Only do it for dev for now * Add change file * Tag respond_async_serialized_dyn with debug
Reference: #13382
From some testing, this could speed up the compile time by ~10s (80s -> 70s) on a full debug build on my machine with a minimal tauri app setup, and reduces the incremental compilation time from ~10s to ~4s (By adding and removing a new line inside an async command)
I'm not entirely sure the performance implications of doing dynamic dispatch for those commands though, maybe we could enable this for the debug build and still use the old static dispatch one for the release build? (did this for dev only right now, we can revisit this in the future)
Some
cargo llvm-linesoutputsBefore
After
Note: the zip file contains the
cargo build --timingsHTML output, and because GitHub doesn't seem to like uploading HTML files directly, I zipped them incargo-build-timings.zip