-
-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Deterministic query cycles for parallel front-end #149849
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
Open
zetanumbers
wants to merge
11
commits into
rust-lang:main
Choose a base branch
from
zetanumbers:deterministic_cycles
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
9efc576
Implement TreeNodeIndex
zetanumbers ffa61e0
Integrate QueryInclusion into QueryJob
zetanumbers 7b8a30b
Move par_fns, par_join, par_slice and others to rustc_middle
zetanumbers 1d02e90
Implement TreeNodeIndex tracking for parallel interfaces
zetanumbers f466828
Replace beack_query_cycles implementation with new
zetanumbers 5f69161
Add TreeNodeIndex unit tests
zetanumbers 6c9954e
Optimize TreeNodeIndex with wrapping operations (checked in godbolt.org)
zetanumbers ee40fee
Get rid of BranchingError as it is no longer used
zetanumbers cf6f16a
Expand comments for TreeNodeIndex
zetanumbers 6e39f24
Clarify how TreeNodeIndex could exhaust its u64 encoding and panic
zetanumbers 15674b4
Do small refactor and add comments in rustc_middle::sync
zetanumbers 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
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,105 @@ | ||
| /// Ordered index for dynamic trees | ||
nnethercote marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// | ||
| /// ## Encoding | ||
| /// | ||
| /// You can index any node of a binary tree with a variable-length bitstring. | ||
| /// Each bit should represent a branch/edge to traverse up from root to down to indexed node. | ||
| /// To encode a variable-length bitstring we use bits of u64 ordered from highest to lowest. | ||
| /// Right after the encoded sequence of bits we set one bit high to recover sequence's length: | ||
| /// | ||
| /// ```text | ||
| /// 0bXXXXXXX100000000...0 | ||
| /// ``` | ||
| /// | ||
| /// The reached node after traversal of `LRLRRLLR` branches (`L` for left, `R` for right) should be | ||
| /// represented as `0b0101100110000...0`. | ||
| /// Root is encoded as `0b10000...0` from an empty bitstring we don't need to traverse any branch | ||
| /// to reach a binary tree's root. | ||
| /// | ||
| /// Here are some examples: | ||
| /// | ||
| /// ```text | ||
| /// (root) -> 0b10000000...0 | ||
| /// L (left) -> 0b01000000...0 | ||
| /// R (right) -> 0b11000000...0 | ||
| /// LL -> 0b00100000...0 | ||
| /// RLR -> 0b10110000...0 | ||
| /// LRL -> 0b01010000...0 | ||
| /// LRRLR -> 0b01101100...0 | ||
| /// ``` | ||
| /// | ||
| /// ## Multi-way tree | ||
| /// | ||
| /// But we don't necessary need to encode a binary tree directly. | ||
| /// We can imagine some node to have `N` number of branches instead of two: right and left. | ||
| /// We encode `0 <= i < N` numbered branches by interpreting `i`'s binary representation as | ||
| /// bitstring for a binary tree traversal. | ||
| /// | ||
| /// For example `N = 3`. Notice how right-most leaf node is unused: | ||
| /// | ||
| /// ```text | ||
| /// root | ||
| /// root / \ | ||
| /// / | \ => . . | ||
| /// 0 1 2 / \ / \ | ||
| /// 0 1 2 - | ||
| /// ``` | ||
| /// | ||
| /// ## Order | ||
| /// | ||
| /// Encoding allows to sort nodes in `left < parent < right` linear order. | ||
| /// If you only consider leaves of a tree then those are sorted in order `left < right`. | ||
| /// | ||
| /// ## Used in | ||
| /// | ||
| /// Primary purpose of `TreeNodeIndex` is to track order of parallel tasks of functions like | ||
| /// `par_join`, `par_slice`, and others (see `rustc_middle::sync`). | ||
| /// This is done in query cycle handling code to determine **intended** first task for a | ||
| /// single-threaded compiler front-end to execute even while multi-threaded. | ||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
| pub struct TreeNodeIndex(u64); | ||
|
|
||
| impl TreeNodeIndex { | ||
| /// Root node of a tree | ||
| pub const fn root() -> Self { | ||
| Self(0x80000000_00000000) | ||
| } | ||
|
|
||
| /// Append branch `i` out of `n` branches total to `TreeNodeIndex`'s traversal representation. | ||
| /// | ||
| /// This method reserves `ceil(log2(n))` bits within `TreeNodeIndex`'s integer encoded | ||
| /// bitstring. | ||
| pub fn branch(self, i: u64, n: u64) -> TreeNodeIndex { | ||
| debug_assert!(i < n, "i = {i} should be less than n = {n}"); | ||
| // `n != 0` per debug assertion above | ||
| let bits = ceil_ilog2(n); | ||
| let trailing_zeros = self.0.trailing_zeros(); | ||
|
|
||
| // For this panic to happen there has to be a recursive function that isn't a query and | ||
| // uses par_join or par_slice recursively. | ||
| // Each query starts with a fresh binary tree, so we can expect this to never happen. | ||
| // That is unless someone writes 64 nested par_join calls or something equivalent. | ||
| let allocated_shift = trailing_zeros.checked_sub(bits).expect( | ||
| "TreeNodeIndex's free bits have been exhausted, make sure recursion is used carefully", | ||
| ); | ||
|
|
||
| // Using wrapping operations for optimization, as edge cases are unreachable: | ||
| // - `trailing_zeros < 64` as we are guaranteed at least one bit is set | ||
| // - `allocated_shift == trailing_zeros - bits <= trailing_zeros < 64` | ||
| TreeNodeIndex( | ||
| self.0 & !u64::wrapping_shl(1, trailing_zeros) | ||
| | u64::wrapping_shl(1, allocated_shift) | ||
| | i.unbounded_shl(allocated_shift.wrapping_add(1)), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| #[inline] | ||
| fn ceil_ilog2(branch_num: u64) -> u32 { | ||
| // Using `wrapping_sub` for optimization, consider `log(0)` to be undefined | ||
| // `floor(log2(n - 1)) + 1 == ceil(log2(n))` | ||
| branch_num.wrapping_sub(1).checked_ilog2().map_or(0, |b| b.wrapping_add(1)) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests; | ||
45 changes: 45 additions & 0 deletions
45
compiler/rustc_data_structures/src/tree_node_index/tests.rs
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,45 @@ | ||
| use crate::tree_node_index::TreeNodeIndex; | ||
|
|
||
| #[test] | ||
| fn up_to_16() { | ||
| for n in 1..128 { | ||
| for i in 0..n { | ||
| TreeNodeIndex::root().branch(i, n).branch(n - i - 1, n); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn ceil_log2() { | ||
| const EVALUATION_TABLE: [(u64, u32); 9] = | ||
| [(1, 0), (2, 1), (3, 2), (4, 2), (5, 3), (6, 3), (7, 3), (8, 3), (u64::MAX, 64)]; | ||
| for &(x, y) in &EVALUATION_TABLE { | ||
| let r = super::ceil_ilog2(x); | ||
| assert!(r == y, "ceil_ilog2({x}) == {r} != {y}"); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn some_cases() { | ||
| let mut tni = TreeNodeIndex::root(); | ||
| tni = tni.branch(0xDEAD, 0xFADE); | ||
| assert_eq!(tni.0, 0xDEAD8000_00000000); | ||
| tni = tni.branch(0xBEEF, 0xCCCC); | ||
| assert_eq!(tni.0, 0xDEADBEEF_80000000); | ||
| tni = tni.branch(1, 2); | ||
| assert_eq!(tni.0, 0xDEADBEEF_C0000000); | ||
| tni = tni.branch(0, 2); | ||
| assert_eq!(tni.0, 0xDEADBEEF_A0000000); | ||
| tni = tni.branch(3, 4); | ||
| assert_eq!(tni.0, 0xDEADBEEF_B8000000); | ||
| tni = tni.branch(0xAAAAAA, 0xBBBBBB); | ||
| assert_eq!(tni.0, 0xDEADBEEF_BAAAAAA8); | ||
| } | ||
|
|
||
| #[test] | ||
| fn edge_cases() { | ||
| const ROOT: TreeNodeIndex = TreeNodeIndex::root(); | ||
| assert_eq!(ROOT.branch(0, 1), TreeNodeIndex::root()); | ||
| assert_eq!(ROOT.branch(u64::MAX >> 1, 1 << 63).0, u64::MAX); | ||
| assert_eq!(ROOT.branch(0, 1 << 63).0, 1); | ||
| } |
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
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.
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 would be helpful in the commit message to add a brief explanation why these functions are being moved. Something like "Because the next commit will modify them to use
ImplicitCtxtwhich is not available inrustc_data_structures."