Skip to content
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

Implement goal caching with the new solver #108071

Merged
merged 2 commits into from
Mar 11, 2023

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Feb 15, 2023

Maybe it's wrong, idk. Opening mostly for first impressions before I go to sleep.

r? @lcnr, cc @cjgillot

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Feb 15, 2023
@compiler-errors compiler-errors changed the title New solver caching Implement goal caching with the new solver Feb 15, 2023
Copy link
Contributor

@lcnr lcnr left a comment

Choose a reason for hiding this comment

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

we currently move all cycle participants to the global cache but should only move the head of the cycle. The other goals still in the provisional cache should get dropped

compiler/rustc_infer/src/traits/mod.rs Outdated Show resolved Hide resolved
}
},
)
let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
Copy link
Contributor

Choose a reason for hiding this comment

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

we should add a comment here, which should state:

  • this is for global caching to correctly track dependencies
  • that we only cache the cycle root and why that should still be fairly performant

can write some of that myself or can chat about it in sync it to correctly summarize the results of https://rust-lang.zulipchat.com/#narrow/stream/364551-t-types.2Ftrait-system-refactor/topic/global.20cache/near/324056794

@lcnr
Copy link
Contributor

lcnr commented Feb 15, 2023

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 15, 2023
);

// FIXME: ???
if i == provisional_entry_index.index() {
Copy link
Member Author

@compiler-errors compiler-errors Feb 16, 2023

Choose a reason for hiding this comment

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

Is this the proper way of checking the head of the cycle? Just the first entry in this drain iterator?

(modulo the type error that currently exists, will fix)

Copy link
Contributor

Choose a reason for hiding this comment

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

jup 👍 though it's probably nicer to first drain all other cycle participants. SO something like

            // If the current goal is the head of a cycle, we drop all other
            // cycle participants without moving them to the global cache.
            let other_cycle_participants = provisional_entry_index.index() + 1;
            for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) {
                let actual_index = cache.lookup_table.remove(&entry.goal);
                debug_assert_eq!(Some(i), actual_index);
                debug_assert!(entry.depth == depth);
            }
            
            let current_goal = cache.entries.pop().unwrap();
            let actual_index = cache.lookup_table.remove(&entry.goal);
            debug_assert_eq!(Some(i), provisional_entry_index.index());
            debug_assert!(entry.depth == depth);
            try_to_global_cache

can we somehow move the pop entries and fix lookup_table into a function 🤔 it's a bit scary that we may forget to do one of them somewhere

Copy link
Member Author

Choose a reason for hiding this comment

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

can we somehow move the pop entries and fix lookup_table into a function

Hm... I can't see how that works with the drain_enumerated, unless we'd write a wrapper around that or something?

@bors
Copy link
Contributor

bors commented Feb 17, 2023

☔ The latest upstream changes (presumably #107965) made this pull request unmergeable. Please resolve the merge conflicts.

@compiler-errors
Copy link
Member Author

Still needs a comment for why we can only cache the root goal 🤔

@compiler-errors compiler-errors marked this pull request as ready for review February 24, 2023 02:41
@rustbot
Copy link
Collaborator

rustbot commented Feb 24, 2023

Some changes occurred to the core trait solver

cc @rust-lang/initiative-trait-system-refactor

// Everything that affects the `Result` should be performed within this
// `with_anon_task` closure.
//
// We only cache the cycle root (why?)
Copy link
Contributor

Choose a reason for hiding this comment

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

The DepGraph is a DAG, so we cannot give it cycles. Therefore, we need a way to represent this cycle (= a SCC in the evaluation graph) as a tree of DepNodes. Those DepNodes must contain as transitive dependencies all the queries that are run while evaluating this SCC.

Later, when replaying an execution, the DepGraph marks the dependencies green/red in their order of apparition. This is important to avoid ICEs. For instance, if the original execution called def_kind to check that a definition was not a closure before calling fn_sig, the replay must realise that def_kind has changed before trying to force fn_sig.

So an SCC's DepNodes must contain as transitive dependencies all the queries that are run while evaluating this SCC, in chronological order.

Having a single DepNode to represent the SCC root (= first goal to have been pushed on the stack) is ok.

Consider a cycle A -> B -> A that we cached as a single DepNode for the cycle root A. The SCC root DepNode represents A.

When replaying, we know that evaluating A makes sense. However, we do not know that the A -> B edge nor B makes sense from just that information, we need to start re-playing the dependencies to know that.

Conversely, we cannot use the same SCC root DepNode to cache B. When re-playing, the invoking code would know B, and the DepGraph would start replaying the execution from A. But the edge B -> A may not exist any more and A may not even make sense.


self.try_move_finished_goal_to_global_cache(tcx, canonical_goal, dep_node);

result
}
Copy link
Contributor

Choose a reason for hiding this comment

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

In terms of code organisation, it may be interesting to have a single function that does all the global cache manipulation (looking at the cached goals, calling the dep-graph and containing the code in try_move_finished_goal_to_global_cache), and eventually generic on the closure that you pass to with_anon_task.

    fn with_new_goal(
        &mut self,
        tcx: TyCtxt<'tcx>,
        canonical_goal: CanonicalGoal<'tcx>,
        evaluate: impl FnMut(&mut Self) -> QueryResult<'tcx>,
    ) -> QueryResult<'tcx> {
        if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) {
            return result;
        }

        match self.try_push_stack(tcx, canonical_goal) { ... }

        let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, evaluate);

        /* inline try_move_finished_goal_to_global_cache */

        result
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

Ended up inlining try_move_finished_goal_to_global_cache here. Also attempted to distill that comment about why we can't cache non-root goals, but not sure if I totally wrap my head around it despite a thorough explanation 😅

@bors
Copy link
Contributor

bors commented Mar 3, 2023

☔ The latest upstream changes (presumably #108707) made this pull request unmergeable. Please resolve the merge conflicts.

@compiler-errors compiler-errors force-pushed the new-solver-caching branch 5 times, most recently from a0a391d to 2959c89 Compare March 10, 2023 04:32
Copy link
Contributor

@lcnr lcnr left a comment

Choose a reason for hiding this comment

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

r=me after extending the comment a bit

not sure if @cjgillot still wants to take another look

// the root goal as that will now always hit the same overflow limit.
//
// NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's
// dependencies, our non-root goal may no longer appear as child of the root goal.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// dependencies, our non-root goal may no longer appear as child of the root goal.
// dependencies, our non-root goal may no longer appear as child of the root goal.
//
// See https://github.com/rust-lang/rust/pull/108071 for some additional
// context.

@compiler-errors
Copy link
Member Author

@bors r=lcnr

gonna approve this for now, happy to do any documentation or refactoring follow-ups later.

@bors
Copy link
Contributor

bors commented Mar 11, 2023

📌 Commit d21e4d8 has been approved by lcnr

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Mar 11, 2023
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Mar 11, 2023
…, r=lcnr

Implement goal caching with the new solver

Maybe it's wrong, idk. Opening mostly for first impressions before I go to sleep.

r? `@lcnr,` cc `@cjgillot`
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 11, 2023
…iaskrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang#104363 (Make `unused_allocation` lint against `Box::new` too)
 - rust-lang#106633 (Stabilize `nonzero_min_max`)
 - rust-lang#106844 (allow negative numeric literals in `concat!`)
 - rust-lang#108071 (Implement goal caching with the new solver)
 - rust-lang#108542 (Force parentheses around `match` expression in binary expression)
 - rust-lang#108690 (Place size limits on query keys and values)
 - rust-lang#108708 (Prevent overflow through Arc::downgrade)
 - rust-lang#108739 (Prevent the `start_bx` basic block in codegen from having two `Builder`s at the same time)
 - rust-lang#108806 (Querify register_tools and post-expansion early lints)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 244ec84 into rust-lang:master Mar 11, 2023
@rustbot rustbot added this to the 1.70.0 milestone Mar 11, 2023
@lcnr lcnr mentioned this pull request Mar 29, 2023
14 tasks
@compiler-errors compiler-errors deleted the new-solver-caching branch August 11, 2023 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants