-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Batch mode driver work (almost usable?) #14525
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
@swift-ci please test |
lib/Driver/Job.cpp
Outdated
assert(&DerivedOutputMap == &other.DerivedOutputMap); | ||
Inputs.append(other.Inputs.begin(), | ||
other.Inputs.end()); | ||
types::forAllTypes([&](types::ID Type) { |
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.
SmallSet has an iterator-based insert
which will be equivalent.
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, as a sink; but it doesn't have an accessor to give you iterator(s) as a source!
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.
Oh, bah! Okay then.
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.
That said, if you don't care about adding to an existing map, you could just do a copy-assignment.
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.
In this case we're doing a merge, the moral equivalent of +=, so I do care about the existing entries (if I'm understanding your comment correctly?)
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 understand that the operation is written as +=
, but at the only place you use it it'll be empty or identical anyway, right?
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.
Oh, fair point. I'll modify it to assert that.
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 is not as intrusive as I feared! Some comments so far, but mostly this looks like it will work, while not getting in the way of non-batch mode.
include/swift/Driver/ToolChain.h
Outdated
@@ -166,6 +166,27 @@ class ToolChain { | |||
std::unique_ptr<CommandOutput> output, | |||
const OutputInfo &OI) const; | |||
|
|||
/// Return true iff the input \c Job \p A is an acceptable candidate for | |||
/// batching together into a BatchJob, via a call to \c constructBathJob. This |
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.
Very relaxing typo: constructBathJob
.
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.
Hah, fixed.
lib/Driver/ToolChain.cpp
Outdated
@@ -150,6 +151,150 @@ types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const { | |||
return types::lookupTypeForExtension(Ext); | |||
} | |||
|
|||
// Return a _single_ TY_Swift InputAction, if one exists; | |||
// if 0 or >1 such inputs exist, return nullptr. |
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.
Doc comment?
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.
Sure I guess? (do static functions in implementation files get docs extracted to anywhere useful?)
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.
Code completion and Quick Help will pick them up in Xcode.
lib/Driver/ToolChain.cpp
Outdated
return false; | ||
|
||
// Check Jobs have same executable. | ||
if (strcmp(A->getExecutable(), B->getExecutable()) != 0) |
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 don't think CompileJobActions will ever be allowed to pick different executables, so maybe this can just be an assertion.
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.
k, fixed.
include/swift/Driver/ToolChain.h
Outdated
/// This is true when each job independently satisfies \c jobIsBatchable, and the | ||
/// jobs have identical executables, output types and environments (i.e. they are | ||
/// identical aside from their inputs). | ||
bool jobsAreBatchCombinable(const Job *A, const Job *B) const; |
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 think it makes sense to require that this be an equivalence relation, so as not to (supposedly) need quadratic checking at the use site.
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.
Hm, fair; I think it technically is one already, I was just describing it as a pairwise relation out of paranoia. I'll change the doc comment to reflect the reality.
lib/Driver/ToolChain.cpp
Outdated
if (strcmp(A->getExecutable(), B->getExecutable()) != 0) | ||
return false; | ||
|
||
// Check Jobs are making the same kind of output. |
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.
Arguably they have to be making the same supplementary outputs too.
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, fixed.
lib/Driver/Compilation.cpp
Outdated
if (Comp.ShowJobLifecycle) | ||
llvm::outs() << "Batch job finished: " | ||
<< LogJob(FinishedCmd) << "\n"; | ||
auto const *B = static_cast<const BatchJob *>(FinishedCmd); |
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 just realized Job needs a virtual destructor if BatchJob is going to contain a SmallVector.
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.
Whoops, fixed!
lib/Driver/Compilation.cpp
Outdated
@@ -410,6 +449,7 @@ namespace driver { | |||
SmallVector<const Job *, 16> Dependents; | |||
if (Comp.getIncrementalBuildEnabled()) { | |||
reloadAndRemarkDeps(FinishedCmd, ReturnCode, Dependents); | |||
formBatchJobsAndAddPendingJobsToTaskQueue(); |
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 had to go figure out how reloadAndRemarkDeps
could schedule new jobs. Maybe there should be a comment around that. (My fault, not yours.)
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.
Fixing. Though .. there's something a little hinky here that I was kinda papering over and I'm still uncomfortable with: we should pick up the extra pending jobs and requeue them in the outer loop (runTaskQueueToCompletion); I'm going to take another look and see if I can figure out why were weren't (there's a single test that fails without one of these seemingly-redundant formBatchJobsAndAddPendingJobsToTaskQueue calls).
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.
Oh gosh, ok, found the underlying issue here and it's both quite subtle and would have been pretty serious if I'd let it go.
(I put a couple comments on the reload path anyways, but it's not the culprit)
lib/Driver/ToolChain.cpp
Outdated
// with a CompileJobAction and JobContext that differ only slightly. | ||
|
||
if (jobs.size() == 0) | ||
return nullptr; |
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.
If it's 1, worth early-exiting with the single Job?
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.
Hm, I don't think so; but I do think we should guard against getting to this path with any number of jobs < 2. I'll do a little refactoring in the caller and put an assert in here.
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.
Eh actually this just moves ugliness in the logic elsewhere, I'm going to leave it as-is.
@swift-ci please test |
@jrose-apple pushed update with review commentary addressed (I think!), with particular emphasis on 91aa7e4 which was actually quite a serious problem -- those seemingly-redundant calls to formBatchJobsAndAddPendingJobsToTaskQueue were not-very-thoroughly compensating for PendingExecution jobs being dropped on the floor in the outer loop -- but I believe I have set it right now. Added a bunch of commentary; let me know if it's not clear. (The remaining changes are mostly tidying, commenting and splitting functions up as sensibly requested by @davidungar; again, I don't really know that the batching code works at the moment since there isn't frontend code quite ready to connect it to, and it's disabled by default anyways; but it seems about as logically reasonable as it was in its previous iteration which we did manage to test in prototype form, and it at least compiles?) |
Build failed |
@@ -17,6 +17,7 @@ | |||
#ifndef SWIFT_DRIVER_COMPILATION_H | |||
#define SWIFT_DRIVER_COMPILATION_H | |||
|
|||
#include "swift/Driver/Driver.h" |
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.
Nitpick: this include seems to be unnecessary.
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's needed to get the complete type for driver::OutputInfo.
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.
Oh, I didn't realize that didn't have its own header. Carry on.
lib/Driver/Compilation.cpp
Outdated
auto const &TC = Comp.getToolChain(); | ||
if (TC.jobsAreBatchCombinable(Comp, Cmd, CurrentBatch[0])) | ||
return true; | ||
return CurrentBatch.size() < TargetBatchSize; |
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 looks like these conditions are backwards: you're doing an OR rather than an AND check here.
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.
Ah good catch.
lib/Driver/Job.cpp
Outdated
@@ -181,6 +193,8 @@ CommandOutput::dump() const { | |||
llvm::errs() << '\n'; | |||
} | |||
|
|||
Job::~Job() { |
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.
Nitpick: You can write this as = default
for the same effect.
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.
Ok, fixed.
lib/Driver/ToolChain.cpp
Outdated
ToolChain::jobsAreBatchCombinable(const Compilation &C, | ||
const Job *A, const Job *B) const { | ||
return (jobIsBatchable(C, A) && | ||
jobIsBatchable(C, B) && |
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.
To avoid redundant checking, you could assert these conditions instead.
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.
Sure, fixed.
lib/Driver/Job.cpp
Outdated
assert(&DerivedOutputMap == &other.DerivedOutputMap); | ||
Inputs.append(other.Inputs.begin(), | ||
other.Inputs.end()); | ||
types::forAllTypes([&](types::ID Type) { |
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.
That said, if you don't care about adding to an existing map, you could just do a copy-assignment.
c93497e
to
394c5a9
Compare
394c5a9
to
7686a5a
Compare
@swift-ci please test |
Build failed |
@jrose-apple fixed issues discussed so far, squashed and rebased. |
Build failed |
// there are, we need to continue trying to make progress on the | ||
// TaskQueue before we start marking deferred jobs as skipped, below. | ||
if (!PendingExecution.empty() && Result == 0) { | ||
formBatchJobsAndAddPendingJobsToTaskQueue(); |
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 suppose if this still bugs you another option is to put it at the top of the loop.
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.
Before the call to TaskQueue::execute? I .. am pretty sure that wouldn't work.
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 doesn't bug me enough to carry on fiddling it further; I think there are about 2 variants that work right and they're similarly confusing. One has two calls, the other needs more explanation, shrug)
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.
Yeah, it would also require changing the loop's controlling condition. This is fine.
This includes the remaining batch mode driver components from the prototype cleaned up to the point where I think they're credible to land.
It's very lightly fiddled-with; I wouldn't even say tested yet. But it's also disabled by default in its disabled state, appears not to perturb tests (at least on linux -- checking macos now). I just wanted to get all this in place so David has something he can play with (since it appears I might get pulled off again to do other things for another few days next week).
It's also missing, of course, support for writing OFMs to frontends, or randomizing batch partition, or probably a dozen forms of failure management I haven't covered yet.