-
Notifications
You must be signed in to change notification settings - Fork 205
Refactor IncrementalCompilationState
to compute initial state before job-generation.
#621
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
Conversation
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.
Testing, one two three
) throws { | ||
guard driver.shouldAttemptIncrementalCompilation else { return nil } |
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.
Not your doing, but in a prior PR, someone else moved shouldAttemptIncrementalCompilation
into the driver, which diffused the knowledge about incrementality. It would be great to find a better place for it.
guard shouldAttemptIncrementalCompilation else { return nil } | ||
|
||
guard let outputFileMap = outputFileMap else { | ||
diagnosticEngine.emit(.warning_incremental_requires_output_file_map) | ||
return nil | ||
} | ||
|
||
let reporter : IncrementalCompilationState.Reporter? | ||
if options.contains(.showIncremental) { | ||
reporter = IncrementalCompilationState.Reporter(diagnosticEngine: diagnosticEngine, | ||
outputFileMap: outputFileMap) | ||
} else { | ||
reporter = nil | ||
} | ||
|
||
guard let buildRecordInfo = buildRecordInfo else { | ||
reporter?.reportDisablingIncrementalBuild("no build record path") | ||
return nil | ||
} | ||
|
||
// FIXME: This should work without an output file map. We should have | ||
// another way to specify a build record and where to put intermediates. | ||
let maybeBuildRecord = | ||
buildRecordInfo.populateOutOfDateBuildRecord(inputFiles: inputFiles, | ||
reporter: reporter) | ||
|
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.
This code encapsulates knowledge about what incremental builds need: an output file map, a build record info, etc. How about moving it (or much of it) into the InitialStateComputer
, either the constructor, or in a static function?
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.
Agreed, it is now a static method on IncrementalCompilationState
.
} | ||
|
||
/// Builds the `InitialState` | ||
/// Also bundles up an bunch of configuration info | ||
extension IncrementalCompilationState { | ||
|
||
public struct InitialStateComputer { |
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.
Not your doing, at all, but I really want to rename the InitialStateComputer
. It's my name and a horrible one. "Initial" and "state" are way too generic, and "computer" isn't much better. What is it really doing? Looking at the inputs, constructing a graph. Is it setting up for an incremental build? IncrementalBuildSetup
? InputChangeAndDependencyGraphConstructor
? Can you come up with something good?
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.
Tentatively going with IncrementalDependencyAndInputSetup
, I think it better reflects what it is computing. 😬
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.
yes!
@@ -140,7 +118,6 @@ public final class IncrementalCompilationState { | |||
} | |||
|
|||
// MARK: - Initial State | |||
|
|||
extension IncrementalCompilationState { | |||
/// The initial state of an incremental compilation plan. | |||
@_spi(Testing) public struct InitialState { |
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.
While I'm kvetching, I'm wondering if this struct is pulling its own weight?
@@ -136,6 +141,7 @@ extension Driver { | |||
) | |||
} | |||
|
|||
// Extract options relevant to incremental builds | |||
mutating func computeIncrementalOptions() -> IncrementalCompilationState.Options { |
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.
I hate to sound like a broken record, and not your fault, but computeIncrementalOptions
is in the wrong place, too, wrt centralizing the knowledge about incremental builds.
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.
Moved. Thanks for pointing it out.
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.
tx!
moduleDependencyGraph.phase = .updatingAfterCompilation | ||
|
||
|
||
let skippedInputs = computeSkippedCompilationInputs( |
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 feels like this computation belongs in the other place, the initial-wobulator-computer.
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.
As we discussed in the chat: I very much would like to have moved this to the initial state setup, but the reliance of this code on the input->outputs mapping that is a part of compilation jobs prevents us from being able to do so with the current planning/jobs architecture.
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.
Agreed
return skippedInputs | ||
} | ||
|
||
private func sortByCommandLineOrder(_ inputs: Set<TypedVirtualPath>) -> [TypedVirtualPath] { |
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.
same here, feels like the wrong place.
|
||
/// Encapsulates information about an input the driver has determined has | ||
/// changed in a way that requires an incremental rebuild. | ||
struct ChangedInput { |
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.
wrong place?
} | ||
|
||
// Find the inputs that have changed since last compilation, or were marked as needed a build | ||
private func computeChangedInputs( |
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.
wrong place?
/// Also bundles up an bunch of configuration info | ||
// Initial incremental state computation | ||
extension Driver { | ||
func computeInitialIncrementalState(options: IncrementalCompilationState.Options) |
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.
Doesn't feel right to put this in the Driver
, not your fault, I know.
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.
Agreed, moved to IncrementalCompilationState
as a static.
259fc05
to
c0e5cf3
Compare
@swift-ci please test |
@davidungar, the current revision addresses the above comments. |
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.
Thank you. Overall much much better, IMO. Just a few small thoughts; see what you think.
@@ -69,81 +69,48 @@ public final class IncrementalCompilationState { | |||
/// be protected by the confinement queue. | |||
private var skippedCompileGroups = [TypedVirtualPath: CompileJobGroup]() | |||
|
|||
// MARK: - Creating IncrementalCompilationState if possible | |||
// MARK: - Creating IncrementalCompilationState if possible | |||
/// Return nil if not compiling incrementally |
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.
Is this comment still correct?
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.
Nope it got stale. Thanks for the catch.
if let initialState = initialIncrementalState { | ||
incrementalCompilationState = | ||
try IncrementalCompilationState(driver: &self, jobsInPhases: jobsInPhases, | ||
initialState: initialState) | ||
} else { | ||
incrementalCompilationState = nil | ||
} |
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.
Nice! Here's an interesting Swift-style question that I wonder about myself:
Is the "if" construction more or less preferable than the "map" construction:
let incrementalCompilationState = initialIncrementalState.map {...}
I think it depends on whether the reader is comfortable/familiar with "map", but I don't know. Coming from C++, one would favor "if", coming from Haskel (which I don't know), one might favor "map"?
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.
Feel free to choose either--just mentioning it.
@@ -104,7 +137,8 @@ extension Driver { | |||
|
|||
try addPrecompileModuleDependenciesJobs(addJob: addJobBeforeCompiles) |
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.
BTW, nice name!
try IncrementalCompilationState.computeIncrementalStateForPlanning(driver: &self) | ||
|
||
// Compute all jobs required for this module | ||
let jobsInPhases = try computeBuildJobsInPhases() |
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.
Nit: computeBuildJobsInPhases
can be misread as "compute and build the jobs (in phases)".
Maybe something like "computeJobsForPhasedStandardBuild" ??
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.
BTW, I like how this reads with the middle factored out. Thank you!
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.
Made the name more explanatory and updated the comment.
} | ||
|
||
/// Consturct all jobs required for this module | ||
mutating func computeBuildJobsInPhases() throws -> JobsInPhases { |
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.
private?
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.
Good catch, changed.
) | ||
} | ||
|
||
/// Consturct all jobs required for this module |
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.
What does the comment say that the name of the function does not? I would either put more info in the comment, or eliminate it.
Why is this function here? The comment could be:
"After computing the basic incremental state, plan a build as if it were not incremental, so that the plan can be modified for incrementality"
or some such.
return Self(options, outputFileMap, | ||
BuildRecordInfo.mock(diagnosticsEngine, outputFileMap), | ||
nil, nil, [], fileSystem, diagnosticsEngine) |
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.
Nice
My pleasure! |
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.
Much better! Just a few nits you can take or leave.
…e job-generation. In the near future, planning of `PrecompileModuleDependenciesJobs`, for explicit module dependencies, will require access to the incremental state in order to incrementalize expensive actions such as dependency scanning. This change refactors the creation of incremental compilation state in order to move computation of the initial state (dependency graph) to occur before job-generation. The first wave of jobs is then computed after job-generation, using the initial state, and the set of jobs in the plan, as input. - Separate `InitialIncrementalStateComputer` to run early, by the `Driver`, to compute the dependency graph and the set of changed inputs. Rename `InitialIncrementalStateComputer` into `IncrementalDependencyAndInputSetup`. - Introduce `FirstWaveComputer` which the constructor of the `IncrementalCompilationContext` uses to compute the `mandatoryJobsInOrder` set of jobs the executors *must* run.
c0e5cf3
to
ccc4024
Compare
@swift-ci please test |
In the near future, planning of
PrecompileModuleDependenciesJobs
, for explicit module dependencies' compilation jobs, will require access to the incremental state in order to incrementalize expensive actions such as dependency scanning.This change refactors the creation of incremental compilation state in order to move computation of the initial state (dependency graph) to occur before job-generation. The first wave of jobs is then computed after job-generation, using the initial state, and the set of jobs in the plan, as input.
InitialIncrementalStateComputer
to run early, by theDriver
, to compute the dependency graph.FirstWaveComputer
which the the constructor of theIncrementalCompilationContext
uses to compute themandatoryJobsInOrder
set of jobs the executors must run.