Skip to content

Refactor execute_job_incr#154279

Draft
nnethercote wants to merge 6 commits intorust-lang:mainfrom
nnethercote:refactor-execute_job_incr
Draft

Refactor execute_job_incr#154279
nnethercote wants to merge 6 commits intorust-lang:mainfrom
nnethercote:refactor-execute_job_incr

Conversation

@nnethercote
Copy link
Contributor

This PR improves some clumsy control flow in execute_job_incr, and also fixes some inconsistencies the the scopes of start_query and query profiling.

@rustbot rustbot added A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 23, 2026
@Zalathar
Copy link
Member

I feel very uneasy about the start_query change, because the commit message seems to be making an argument on aesthetic/consistency grounds, but doesn't have any discussion of what the change is actually doing, or whether it's correct.

@nnethercote
Copy link
Contributor Author

Ok, so what does start_query do?

  • Maybe checks the query depth limit.
  • Sets up ImplicitCtxt with the given query job id and a (maybe) new depth-limit. These values won't be used unless we end up calling try_execute_query within the given closure.

Let's think about the things that were within start_query and no longer are.

  • Loading a value from disk, query profiling start/stop, and hash verification: these are unaffected because they don't interact with ImplicitCtxt's query job id and depth-limit.
  • try_mark_green: this is the interesting one.
    • It can (eventually) call force_query, which calls try_execute_query, which uses ImplicitCtxt and calls start_query again, and then invoke_provider_fn.
    • So the old code could call start_query twice on the way to a single invoke_provider_fn. This will result in ImplicitCtxt being set for a second time, albeit with the same values. This would mess up the depth limit check, except that false is passed as the second argument to the start_query call in execute_job_incr, so the check is skipped. (This might also explain the comment "The diagnostics for this query will be promoted to the current session during try_mark_green(), so we can ignore them here." Not sure.)
    • So I think the new code is better: we avoid calling start_query twice on the try_mark_green path, avoid the redundant setting of ImplicitCtxt. And that false argument to start_query can be changed to query.depth_limit for another consistency win.

@rust-bors

This comment has been minimized.

@nnethercote nnethercote force-pushed the refactor-execute_job_incr branch from 745e144 to f7fed56 Compare March 24, 2026 06:34
@nnethercote
Copy link
Contributor Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors bot pushed a commit that referenced this pull request Mar 24, 2026
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Mar 24, 2026
Comment on lines +576 to +581
pub fn with_feed_task<'tcx, C: QueryCache>(
&self,
node: DepNode,
tcx: TyCtxt<'tcx>,
result: &R,
hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
format_value_fn: fn(&R) -> String,
query: &'tcx QueryVTable<'tcx, C>,
node: DepNode,
value: &C::Value,
Copy link
Member

Choose a reason for hiding this comment

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

I had considered doing this in the past, and ultimately backed down because I wasn't sure if I wanted to introduce this C: QueryCache bound here or not.

But it's probably fine, and being able to simplify the signature of incremental_verify_ich is certainly a nice payoff, so +1 on this change.

@rust-bors

This comment has been minimized.

@rust-bors
Copy link
Contributor

rust-bors bot commented Mar 24, 2026

☀️ Try build successful (CI)
Build commit: 237f82c (237f82c3b1536e49cf903c29142efb83d1886e35, parent: 212b0d480f337082bbe1132d2b62be20e7e61f8a)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (237f82c): comparison URL.

Overall result: ✅ improvements - no action needed

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

@bors rollup=never
@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.2% [-0.2%, -0.2%] 2
Improvements ✅
(secondary)
-0.2% [-0.3%, -0.1%] 13
All ❌✅ (primary) -0.2% [-0.2%, -0.2%] 2

Max RSS (memory usage)

Results (primary 2.5%, secondary 0.5%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.5% [1.6%, 3.5%] 2
Regressions ❌
(secondary)
3.0% [1.9%, 3.4%] 4
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-4.6% [-6.4%, -2.7%] 2
All ❌✅ (primary) 2.5% [1.6%, 3.5%] 2

Cycles

Results (primary 0.3%, secondary -1.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.5% [2.3%, 2.8%] 2
Regressions ❌
(secondary)
6.0% [6.0%, 6.0%] 1
Improvements ✅
(primary)
-2.0% [-2.5%, -1.4%] 2
Improvements ✅
(secondary)
-3.5% [-4.0%, -2.8%] 3
All ❌✅ (primary) 0.3% [-2.5%, 2.8%] 4

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 495.299s -> 485.474s (-1.98%)
Artifact size: 394.77 MiB -> 394.80 MiB (0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Mar 24, 2026
@nnethercote nnethercote force-pushed the refactor-execute_job_incr branch from f7fed56 to 68a138d Compare March 24, 2026 09:39
There are three `start_query` calls. Two of them have very small scopes,
just an `invoke_provider_fn` call and very little else. But the one in
`execute_job_incr` has a huge scope that covers a `try_mark_green` call
and everything done by `load_from_disk_or_invoke_provider_green`: a disk
load attempt, `prof_timer` start/stop; an `invoke_provider_fn` call, and
hash verification.

This commit moves the `start_query` call into
`load_from_disk_or_invoke_provider_green` to make the scope match the
other two cases.

Most of the code that is moved out of the `start_query` scope is
unaffected. `try_mark_green` is affected, but in a good way. With the
old scope, when going through `try_mark_green` two calls to
`start_query` would occur on the way to an actual `invoke_provider_fn`.
This is why `false` was passed for `depth_limit`; this `false` is no
longer necessary.
It doesn't need to be in there. Removing it makes `define_queries`
smaller, which is always good, and also enables the next commit which
tidies up profiling.

Also change how `value` and `verify` are initialized, because I just
don't like the current way of doing it after the declaration.
From `plumbing.rs` to `execution.rs`, which is where most of the other
query profiling occurs. It also avoids eliminates some fn parameters.

This means the `provided_to_erased` call in `try_from_load_disk_fn` is
now included in the profiling when previously it wasn't. This is
good because `provided_to_erased` is included in other profiling calls
(e.g. calls to `invoke_provider_fn`).
It has a single call site. After inlining we can refactor
`execute_job_incr` quite a bit and the overall result is simpler and
clearer, especially the control flow -- no `try` block in an `if`
condition(!), no early returns, just vanilla `if`s and `match`es and
about -40 lines of code.
Remove a low-value comment, and add a comment about hashing explaining
something that I found non-obvious.
Specifically, `DepGraph::with_feed_task` and `incremental_verify_ich`
This makes some things shorter, at the cost of making module `dep_graph`
depend directly on module `query`. (It already depends indirectly via
module `verify_ich`.)
@Zalathar
Copy link
Member

Zalathar commented Mar 24, 2026

I was looking at the version of execute_job_incr produced by this PR, and I have to say that overall it does not feel like an improvement.

There are now several multi-level nested conditions, else arms that are very far away from their condition, multiple subtly different kinds of cryptic tuple, and no obvious logical flow overall.

The current version, while far from perfect, at least has a mostly-linear structure with a distinct green path followed by fallback to a red path. The new version makes me immediately want to look for ways to make things more manageable.

EDIT: Specifically, the commit that removes the green-path early return and inlines load_from_disk_or_invoke_provider_green feels like a step backwards. I do like trying to make improvements to the weird structure in that block, but I don't like this particular outcome.

@nnethercote nnethercote force-pushed the refactor-execute_job_incr branch from 68a138d to d595192 Compare March 24, 2026 10:01
@nnethercote
Copy link
Contributor Author

Arguing over matters of taste is probably pointless but I'll defend my work in case it stops you from blocking what I think is a good change.

This code is complex because the underlying algorithm is complex. Breaking it in to smaller pieces doesn't change the complexity, it just obscures it. Splitting execute_job_incr gives you two shorter functions that are so tightly intertwined that you have to understand one to understand the other. I would much prefer one coherent 86 line function with all the closely related stuff in it than two awkwardly split functions spanning 122 lines.

Just look at load_from_disk_or_invoke_provider_green. The name alone is a giveaway. It's two different green paths masquerading as one. It has seven parameters. It's the kind of awkward structure you get when someone decides that functions should not exceed a particular number of lines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants