-
Notifications
You must be signed in to change notification settings - Fork 255
Implement support for out-of-process compilation #1536
Conversation
Excellent work, I am super excited about this approach!
My gut feeling would be that optimizing here is not really required: we shouldn't be running many compilations concurrently anyway. I am more worried about depending on tokio and the rest of async ecosystem: it centrally is a good choice for getting the stuff up an running, but, longer term, I think that we are unfortunately buying-in into a lot of complexity here. An alternative would be to roll-our-own blocking json-per-line API on top of mkfifo/windows-named-pipe.
That's perhaps a naive question, but whey we can't do |
Oh, this looks interesting:
https://users.rust-lang.org/t/recommended-way-of-ipc-in-rust/31116/9?u=matklad
…On Wednesday, 7 August 2019, Igor Matuszewski ***@***.***> wrote:
This is quite a lengthy patch, but the gist of it is as follows:
- rls-ipc crate is introduced which acts as the IPC interface along
with a server/client implementation
- rls-rustc is enhanced with optional support for the IPC
- RLS can optionally support it via setting RLS_OUT_OF_PROCESS env var
(like rls-rustc it needs to be compiled ipc feature)
The IPC is async JSON-RPC running on Tokio using parity-tokio-ipc (UDS on
unices and named pipes on Windows)
- Tokio because I wanted to more or less easily come up with a PoC
- RPC because I needed a request->response model for VFS IPC function
calls
- uds/pipes because it's somewhat cross-platform and we don't have to
worry about rustc potentially polluting stdio (maybe just capturing
the output in run_compiler would be enough?)
However, the implementation is far from efficient - it currently starts a
thread per requested compilation, which in turn starts a single-threaded
async runtime to drive the IPC server for a given compilation.
I imagine we could either just initiate the runtime globally and spawn the
servers on it and drive them to completion on each compilation to reduce
the thread spawn/coordination overhead.
While this gets rid of the global environment lock on each (previously)
in-process crate compilation, what still needs to be addressed is the sequential
compilation
<https://github.com/rust-lang/rls/blob/35eba227650eee482bedac7d691a69a8487b2135/rls/src/build/plan.rs#L122-L124>
of cached build plan for this implementation to truly benefit from the
unlocked parallelization potential.
I did some rough test runs (~5) and on a warm cache had the following
results:
- integration test suite (release) 3.6 +- 0.2s (in-process) vs 3.8 +-
0.3s (out-of-process)
- rustfmt master whitespace change (release) 6.4 +- 0.2s (in-process)
vs 6.6 +- 0.3s (out-of-process)
which at least initially confirms that the performance overhead is
somewhat negligible if we can really parallelize the work and leverage
process isolation for increased stability.
cc #1307 <#1307>
(I'll squash the commits in the final merge, 30+ commits is a tad too much
😅 )
If possible I'd like to get more eyes on the patch to see if it's a good
approach and what might be directly improved:
- @matklad <https://github.com/matklad> for potentially shared
rustc-with-patched-filesystem
- @alexheretic <https://github.com/alexheretic> for the
RLS/implementation itself
- @alexcrichton <https://github.com/alexcrichton> @nrc
<https://github.com/nrc> do you have thoughts on if we can share the
parallel graph compilation logic with Cargo somehow? For now we just rolled
our own linear queue here because we didn't need much more but maybe it
might be worthwhile to extract the pure execution bits somehow?
------------------------------
You can view, comment on, or merge this pull request online at:
#1536
Commit Summary
- rls-rustc: Remove redundant sysroot crate imports
- rls-rustc: Scaffold IPC file loader
- rls-rustc: Add PoC to sending data across processes
- Use jsonrpc-ipc-server
- Use patched jsonrpc IPC server
- rls-rustc: Tidy up the implementation
- rls: Implement the IPC server on the RLS side
- WIP: Pass back analysis as IPC callback
- rls-rustc: Adapt to Compilation driver changes
- Capture stderr directly for diagnostics
- Collect file -> edition mapping in out-of-process compilation
- Support Clippy over IPC
- WIP: Bump jsonrpc to git master
- WIP: Add rls-ipc crate
- Use unified IPC interface from rls-ipc
- Clean up IPC interfaces in rls-rustc
- Format and stuff
- Use jsonrpc crate from crates.io
- Use VFS snapshots for out-of-process rustc
- Handle exit codes directly in RLS
- Perform a minor cleanup
- Gate out-of-process RLS behind an environment variable
- Collect files -> editions mapping after expansion when out-of-process
- Wait for the thread the IPC server is spawned on
- rls-rustc: Gate anything Clippy-related behind feature flag
- Unify in and out of process compilation entry point
- Use RlsRustcCalls only in in-process case
- Move in-process compilation specific stuff into relevant function
- Start prettifying code
- Run cargo fmt
- Support not compiling out-of-process compilation
- Tidy up the remaining implementation
File Changes
- *M* Cargo.lock
<https://github.com/rust-lang/rls/pull/1536/files#diff-0> (291)
- *M* Cargo.toml
<https://github.com/rust-lang/rls/pull/1536/files#diff-1> (11)
- *A* rls-ipc/.gitignore
<https://github.com/rust-lang/rls/pull/1536/files#diff-2> (3)
- *A* rls-ipc/Cargo.toml
<https://github.com/rust-lang/rls/pull/1536/files#diff-3> (21)
- *A* rls-ipc/src/client.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-4> (25)
- *A* rls-ipc/src/lib.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-5> (9)
- *A* rls-ipc/src/rpc.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-6> (80)
- *A* rls-ipc/src/server.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-7> (3)
- *M* rls-rustc/Cargo.toml
<https://github.com/rust-lang/rls/pull/1536/files#diff-8> (16)
- *M* rls-rustc/src/bin/rustc.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-9> (4)
- *A* rls-rustc/src/clippy.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-10> (90)
- *A* rls-rustc/src/ipc.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-11> (73)
- *M* rls-rustc/src/lib.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-12> (232)
- *M* rls/src/build/cargo.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-13> (2)
- *A* rls/src/build/ipc.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-14> (163)
- *M* rls/src/build/mod.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-15> (2)
- *M* rls/src/build/plan.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-16> (7)
- *M* rls/src/build/rustc.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-17> (167)
- *M* rls/src/config.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-18> (11)
- *M* rls/src/main.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-19> (6)
- *M* tests/client.rs
<https://github.com/rust-lang/rls/pull/1536/files#diff-20> (1)
Patch Links:
- https://github.com/rust-lang/rls/pull/1536.patch
- https://github.com/rust-lang/rls/pull/1536.diff
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1536?email_source=notifications&email_token=AANB3M665NUXHWRWAQO4UOTQDH4FTA5CNFSM4IJ2ZCZ2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HDX62TA>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AANB3M7U4RQMBT4E2AZVOWLQDH4FTANCNFSM4IJ2ZCZQ>
.
|
This can be done but it'll likely be somewhat tricky. There's a lot to handle here like:
FWIW though I don't have a good understanding of what this PR is doing, so I'm not quite sure exactly how hard this would be! In any case if you're interested we could try to see if logic could be extracted to crates on crates.io or something like that? |
e293bb8
to
4fb6446
Compare
☔ The latest upstream changes (presumably #1541) made this pull request unmergeable. Please resolve the merge conflicts. |
ccb979d
to
a6e0304
Compare
I'm looking forward to the improvements compiling out-of-process can bring. This implementation looks quite clean. I wonder if we could make this simpler though. On that note I wouldn't have bothered with the "ipc" feature separation, it adds complexity. Is this just to avoid compiling tokio when not used? Although having both in-process & out with a runtime switch makes sense. I'd like to have diagnostic streaming instead of after-compile-reporting, and for active processes to be killed/cancelled when a new compile is required. But I'm getting ahead of myself a bit. |
☔ The latest upstream changes (presumably #1545) made this pull request unmergeable. Please resolve the merge conflicts. |
a6e0304
to
3bcfa0f
Compare
Yeah, that's fair - ideally I'd like not to depend on all of it just for the sake of IPC. FWIW we do use Tokio already in the development setting for the integration tests and right now the IPC is conditionally compiled under a feature flag, so for now I'd continue with this approach while we polish it as we go in order to ship it in the RLS for other users.
That's the hottest operation right now in the RLS so I'd like to cut down the overhead as much as possible. I'd prefer not to re-run the
Time to shave some more yaks it seems 😈
That'd be interesting to pursue! In general it'd be great if we could just throw a computation DAG/build plan at an 'execution engine' of sorts and handle the output. As I understand it Rayon (which also has support for jobserver) is meant to be used to process data in a single process rather than coordinate multiple processes? At a first glance this sounds like a useful thing to have in the ecosystem, so maybe it wouldn't be a waste only to expose it for the sake of RLS?
Yep! (see above)
That's a great idea! Let me write that on a to-do list - we should definitely explore it further. Since the implementation looks okay as you're saying I'll merge this and we'll hopefully iterate and polish it further as we go. Thanks for the review ❤️ |
@bors r+ |
📌 Commit 3bcfa0f has been approved by |
Implement support for out-of-process compilation This is quite a lengthy patch, but the gist of it is as follows: - `rls-ipc` crate is introduced which acts as the IPC interface along with a server/client implementation - `rls-rustc` is enhanced with optional support for the IPC - RLS can optionally support it via setting `RLS_OUT_OF_PROCESS` env var (like `rls-rustc` it needs to be compiled `ipc` feature) The IPC is async JSON-RPC running on Tokio using `parity-tokio-ipc` (UDS on unices and named pipes on Windows) - Tokio because I wanted to more or less easily come up with a PoC - RPC because I needed a request->response model for VFS IPC function calls - uds/pipes because it's somewhat cross-platform and we don't have to worry about `rustc` potentially polluting stdio (maybe just capturing the output in `run_compiler` would be enough?) However, the implementation is far from efficient - it currently starts a thread per requested compilation, which in turn starts a single-threaded async runtime to drive the IPC server for a given compilation. I imagine we could either just initiate the runtime globally and spawn the servers on it and drive them to completion on each compilation to reduce the thread spawn/coordination overhead. While this gets rid of the global environment lock on each (previously) in-process crate compilation, what still needs to be addressed is the [sequential compilation](https://github.com/rust-lang/rls/blob/35eba227650eee482bedac7d691a69a8487b2135/rls/src/build/plan.rs#L122-L124) of cached build plan for this implementation to truly benefit from the unlocked parallelization potential. I did some rough test runs (~5) and on a warm cache had the following results: - integration test suite (release) 3.6 +- 0.2s (in-process) vs 3.8 +- 0.3s (out-of-process) - rustfmt master whitespace change (release) 6.4 +- 0.2s (in-process) vs 6.6 +- 0.3s (out-of-process) which at least initially confirms that the performance overhead is somewhat negligible if we can really parallelize the work and leverage process isolation for increased stability. cc #1307 (I'll squash the commits in the final merge, 30+ commits is a tad too much 😅 ) If possible I'd like to get more eyes on the patch to see if it's a good approach and what might be directly improved: - @matklad for potentially shared rustc-with-patched-filesystem - @alexheretic for the RLS/implementation itself - @alexcrichton @nrc do you have thoughts on if we can share the parallel graph compilation logic with Cargo somehow? For now we just rolled our own linear queue here because we didn't need much more but maybe it might be worthwhile to extract the pure execution bits somehow?
☀️ Test successful - checks-azure |
This is quite a lengthy patch, but the gist of it is as follows:
rls-ipc
crate is introduced which acts as the IPC interface along with a server/client implementationrls-rustc
is enhanced with optional support for the IPCRLS_OUT_OF_PROCESS
env var (likerls-rustc
it needs to be compiledipc
feature)The IPC is async JSON-RPC running on Tokio using
parity-tokio-ipc
(UDS on unices and named pipes on Windows)rustc
potentially polluting stdio (maybe just capturing the output inrun_compiler
would be enough?)However, the implementation is far from efficient - it currently starts a thread per requested compilation, which in turn starts a single-threaded async runtime to drive the IPC server for a given compilation.
I imagine we could either just initiate the runtime globally and spawn the servers on it and drive them to completion on each compilation to reduce the thread spawn/coordination overhead.
While this gets rid of the global environment lock on each (previously) in-process crate compilation, what still needs to be addressed is the sequential compilation of cached build plan for this implementation to truly benefit from the unlocked parallelization potential.
I did some rough test runs (~5) and on a warm cache had the following results:
which at least initially confirms that the performance overhead is somewhat negligible if we can really parallelize the work and leverage process isolation for increased stability.
cc #1307
(I'll squash the commits in the final merge, 30+ commits is a tad too much 😅 )
If possible I'd like to get more eyes on the patch to see if it's a good approach and what might be directly improved: