-
Notifications
You must be signed in to change notification settings - Fork 507
Optimization: Vendor jobserver impl and rm thread spawning in parallel compile_objects #889
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
Merged
Merged
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
c3e577f
Impl vendored jobserver implementation
NobodyXu 14c1c9b
Convert parallel `compile_objects` to use future instead of threads
NobodyXu 83eccca
Optimize parallel `compile_objects`
NobodyXu 3ca2a7f
Fix `job_token`: Remove mpsc and make sure tokens are relased
NobodyXu 1f1e857
Optimize `job_token`: Make `JobToken` zero-sized
NobodyXu dfcbae5
Fix `windows::JobServerClient::try_acquire` impl
NobodyXu 19abb40
Fix `unix::JobServerClient::from_pipe`: Accept more fd access modes
NobodyXu 7543bb6
Rm unnecessary `'static` bound in parameter of `job_token`
NobodyXu df659cd
Optimize parallel `compile_objects`: Sleep/yield if no progress is made
NobodyXu 43527dd
Fix windows implementation: Match all return value explicitly
NobodyXu 4f127d2
Use Result::ok() in job_token.rs
NobodyXu 2f1b5aa
Fix grammer in comments
NobodyXu 7bb7e40
simplify job_token impl
NobodyXu 1a45c58
Add more comment explaining the design choice
NobodyXu 31b3480
Refactor: Extract new mod `async_executor`
NobodyXu 5cd8470
Update src/job_token/unix.rs
NobodyXu 34ef631
Remove outdated comment
NobodyXu b06b920
Do not check for `--jobserver-fds` on windows
NobodyXu c36493f
Accept ASCII only in windows `JobServerClient::open` impl
NobodyXu c7f2d74
Use acquire and release ordering for atomic operation in `JobServer`
NobodyXu 4e5536c
Add a TODO for use of `NUM_JOBS`
NobodyXu e7dbd3e
Simplify windows jobserver `WAIT_ABANDONED` errmsg
NobodyXu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
use std::{ | ||
cell::Cell, | ||
future::Future, | ||
pin::Pin, | ||
ptr, | ||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, | ||
thread, | ||
time::Duration, | ||
}; | ||
|
||
use crate::Error; | ||
|
||
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( | ||
// Cloning just returns a new no-op raw waker | ||
|_| NOOP_RAW_WAKER, | ||
// `wake` does nothing | ||
|_| {}, | ||
// `wake_by_ref` does nothing | ||
|_| {}, | ||
// Dropping does nothing as we don't allocate anything | ||
|_| {}, | ||
); | ||
const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE); | ||
|
||
#[derive(Default)] | ||
pub(super) struct YieldOnce(bool); | ||
|
||
impl Future for YieldOnce { | ||
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { | ||
let flag = &mut std::pin::Pin::into_inner(self).0; | ||
if !*flag { | ||
*flag = true; | ||
Poll::Pending | ||
} else { | ||
Poll::Ready(()) | ||
} | ||
} | ||
} | ||
|
||
/// Execute the futures and return when they are all done. | ||
/// | ||
/// Here we use our own homebrew async executor since cc is used in the build | ||
/// script of many popular projects, pulling in additional dependencies would | ||
/// significantly slow down its compilation. | ||
pub(super) fn block_on<Fut1, Fut2>( | ||
mut fut1: Fut1, | ||
mut fut2: Fut2, | ||
has_made_progress: &Cell<bool>, | ||
) -> Result<(), Error> | ||
where | ||
Fut1: Future<Output = Result<(), Error>>, | ||
Fut2: Future<Output = Result<(), Error>>, | ||
{ | ||
// Shadows the future so that it can never be moved and is guaranteed | ||
// to be pinned. | ||
// | ||
// The same trick used in `pin!` macro. | ||
// | ||
// TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!` | ||
let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) }); | ||
let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) }); | ||
|
||
// TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version | ||
// which it is stablised, replace this wth `Waker::noop`. | ||
let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) }; | ||
let mut context = Context::from_waker(&waker); | ||
|
||
let mut backoff_cnt = 0; | ||
|
||
loop { | ||
has_made_progress.set(false); | ||
|
||
if let Some(fut) = fut2.as_mut() { | ||
if let Poll::Ready(res) = fut.as_mut().poll(&mut context) { | ||
fut2 = None; | ||
res?; | ||
} | ||
} | ||
|
||
if let Some(fut) = fut1.as_mut() { | ||
if let Poll::Ready(res) = fut.as_mut().poll(&mut context) { | ||
fut1 = None; | ||
res?; | ||
} | ||
} | ||
|
||
if fut1.is_none() && fut2.is_none() { | ||
return Ok(()); | ||
} | ||
|
||
if !has_made_progress.get() { | ||
if backoff_cnt > 3 { | ||
// We have yielded at least three times without making' | ||
// any progress, so we will sleep for a while. | ||
let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10)); | ||
thread::sleep(duration); | ||
} else { | ||
// Given that we spawned a lot of compilation tasks, it is unlikely | ||
// that OS cannot find other ready task to execute. | ||
// | ||
// If all of them are done, then we will yield them and spawn more, | ||
// or simply return. | ||
// | ||
// Thus this will not be turned into a busy-wait loop and it will not | ||
// waste CPU resource. | ||
thread::yield_now(); | ||
} | ||
} | ||
|
||
backoff_cnt = if has_made_progress.get() { | ||
0 | ||
} else { | ||
backoff_cnt + 1 | ||
}; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.