42
42
43
43
#include " CompilationRecord.h"
44
44
45
+ // Batch-mode has a sub-mode for testing that randomizes batch partitions,
46
+ // by user-provided seed. That is the only thing randomized here.
47
+ #include < random>
48
+
45
49
using namespace swift ;
46
50
using namespace swift ::sys;
47
51
using namespace swift ::driver;
@@ -97,6 +101,7 @@ Compilation::Compilation(DiagnosticEngine &Diags,
97
101
unsigned NumberOfParallelCommands,
98
102
bool EnableIncrementalBuild,
99
103
bool EnableBatchMode,
104
+ unsigned BatchSeed,
100
105
bool SkipTaskExecution,
101
106
bool SaveTemps,
102
107
bool ShowDriverTimeCompilation,
@@ -112,6 +117,7 @@ Compilation::Compilation(DiagnosticEngine &Diags,
112
117
SkipTaskExecution(SkipTaskExecution),
113
118
EnableIncrementalBuild(EnableIncrementalBuild),
114
119
EnableBatchMode(EnableBatchMode),
120
+ BatchSeed(BatchSeed),
115
121
SaveTemps(SaveTemps),
116
122
ShowDriverTimeCompilation(ShowDriverTimeCompilation),
117
123
Stats(std::move(StatsReporter)) {
@@ -120,7 +126,8 @@ Compilation::Compilation(DiagnosticEngine &Diags,
120
126
static bool writeFilelistIfNecessary (const Job *job, DiagnosticEngine &diags);
121
127
122
128
using CommandSet = llvm::SmallPtrSet<const Job *, 16 >;
123
-
129
+ using CommandSetVector = llvm::SetVector<const Job*>;
130
+ using BatchPartition = std::vector<std::vector<const Job*>>;
124
131
125
132
using InputInfoMap = llvm::SmallMapVector<const llvm::opt::Arg *,
126
133
CompileJobAction::InputInfo, 16 >;
@@ -140,7 +147,7 @@ namespace driver {
140
147
// / A temporary buffer to hold commands that were scheduled but haven't been
141
148
// / added to the Task Queue yet, because we might try batching them together
142
149
// / first.
143
- CommandSet PendingExecution;
150
+ CommandSetVector PendingExecution;
144
151
145
152
// / Set of synthetic BatchJobs that serve to cluster subsets of jobs waiting
146
153
// / in PendingExecution. Also used to identify (then unpack) BatchJobs back
@@ -680,7 +687,8 @@ namespace driver {
680
687
681
688
// / Insert all jobs in \p Cmds (of descriptive name \p Kind) to the \c
682
689
// / TaskQueue, and clear \p Cmds.
683
- void transferJobsToTaskQueue (CommandSet &Cmds, StringRef Kind) {
690
+ template <typename Container>
691
+ void transferJobsToTaskQueue (Container &Cmds, StringRef Kind) {
684
692
for (const Job *Cmd : Cmds) {
685
693
if (Comp.ShowJobLifecycle )
686
694
llvm::outs () << " Adding " << Kind
@@ -694,8 +702,8 @@ namespace driver {
694
702
// / Partition the jobs in \c PendingExecution into those that are \p
695
703
// / Batchable and those that are \p NonBatchable, clearing \p
696
704
// / PendingExecution.
697
- void getPendingBatchableJobs (CommandSet &Batchable,
698
- CommandSet &NonBatchable) {
705
+ void getPendingBatchableJobs (CommandSetVector &Batchable,
706
+ CommandSetVector &NonBatchable) {
699
707
for (const Job *Cmd : PendingExecution) {
700
708
if (Comp.getToolChain ().jobIsBatchable (Comp, Cmd)) {
701
709
if (Comp.ShowJobLifecycle )
@@ -710,49 +718,83 @@ namespace driver {
710
718
PendingExecution.clear ();
711
719
}
712
720
713
- // / If \p CurrentBatch is nonempty, construct a new \c BatchJob from its
721
+ // / If \p Batch is nonempty, construct a new \c BatchJob from its
714
722
// / contents by calling \p ToolChain::constructBatchJob, then insert the
715
- // / new \c BatchJob into \p Batches and clear \p CurrentBatch .
723
+ // / new \c BatchJob into \p Batches.
716
724
void
717
- formBatchJobFromCurrentBatch (CommandSet &Batches,
718
- llvm::SetVector <const Job *> &CurrentBatch ) {
719
- if (CurrentBatch .empty ())
725
+ formBatchJobFromPartitionBatch (std::vector< const Job *> &Batches,
726
+ std::vector <const Job *> const &Batch ) {
727
+ if (Batch .empty ())
720
728
return ;
721
729
if (Comp.ShowJobLifecycle )
722
730
llvm::outs () << " Forming batch job from "
723
- << CurrentBatch .size () << " constituents\n " ;
731
+ << Batch .size () << " constituents\n " ;
724
732
auto const &TC = Comp.getToolChain ();
725
- auto J = TC.constructBatchJob (CurrentBatch. getArrayRef () , Comp);
733
+ auto J = TC.constructBatchJob (Batch , Comp);
726
734
if (J)
727
- Batches.insert (Comp.addJob (std::move (J)));
728
- CurrentBatch.clear ();
735
+ Batches.push_back (Comp.addJob (std::move (J)));
729
736
}
730
737
731
- // / Return true iff \p Cmd can be expanded by \p CurrentBatch, meaning
732
- // / that \p CurrentBatch is smaller than \p TargetBatchSize and \p Cmd
733
- // / is batch-combinable with the equivalence class of \p CurrentBatch
734
- // / (as represented by element 0 of \p CurrentBatch).
735
- bool canExpandBatch (const Job *Cmd,
736
- llvm::SetVector<const Job *> &CurrentBatch,
737
- size_t TargetBatchSize) {
738
- auto const &TC = Comp.getToolChain ();
739
- return (CurrentBatch.empty () ||
740
- (TC.jobsAreBatchCombinable (Comp, Cmd, CurrentBatch[0 ]) &&
741
- CurrentBatch.size () < TargetBatchSize));
738
+ // / Inspect current batch \p i of the \p Partition currently being built
739
+ // / and, if that batch is "full" (in the sense of holding an evenly-divided
740
+ // / portion of NumJobs) then advance \p i to the next batch index in the
741
+ // / partition.
742
+ void maybeAdvanceToNextPartition (size_t &i,
743
+ BatchPartition const &Partition,
744
+ size_t NumJobs) {
745
+ assert (i < Partition.size ());
746
+ size_t Remainder = NumJobs % Partition.size ();
747
+ size_t TargetSize = NumJobs / Partition.size ();
748
+ // Spread remainder evenly across partitions by adding 1 to the target
749
+ // size of the first Remainder of them.
750
+ if (i < Remainder)
751
+ TargetSize++;
752
+ if (Partition[i].size () >= TargetSize)
753
+ ++i;
754
+ assert (i < Partition.size ());
742
755
}
743
756
744
- // / If \p CurrentBatch can't be expanded with \p Cmd, form a new \c BatchJob
745
- // / from \p CurrentBatch, add it to \p Batches, and reset\p CurrentBatch;
746
- // / then in either case, insert \p Cmd into \p CurrentBatch.
747
- void expandBatch (const Job *Cmd,
748
- CommandSet &Batches,
749
- llvm::SetVector<const Job *> &CurrentBatch,
750
- size_t TargetBatchSize) {
751
- if (!canExpandBatch (Cmd, CurrentBatch, TargetBatchSize)) {
752
- formBatchJobFromCurrentBatch (Batches, CurrentBatch);
757
+ // / Shuffle \p Batchable if -driver-batch-seed is nonzero.
758
+ void maybeShuffleBatchable (std::vector<const Job *> &Batchable) {
759
+ if (Comp.BatchSeed != 0 ) {
760
+ std::minstd_rand gen (Comp.BatchSeed );
761
+ std::shuffle (Batchable.begin (), Batchable.end (), gen);
762
+ }
763
+ }
764
+
765
+ // / Create \c NumberOfParallelCommands batches and assign each job to a
766
+ // / batch either filling each partition in order or, if seeded with a
767
+ // / nonzero value, pseudo-randomly (but determinstically and nearly-evenly).
768
+ void partitionIntoBatches (std::vector<const Job *> Batchable,
769
+ BatchPartition &Partition) {
770
+ if (Comp.ShowJobLifecycle ) {
771
+ llvm::outs () << " Found " << Batchable.size () << " batchable jobs\n " ;
772
+ llvm::outs () << " Forming into " << Partition.size () << " batches\n " ;
773
+ }
774
+
775
+ assert (Partition.size () > 0 );
776
+ maybeShuffleBatchable (Batchable);
777
+
778
+ size_t i = 0 ;
779
+ auto const &TC = Comp.getToolChain ();
780
+ for (const Job *Cmd : Batchable) {
781
+ maybeAdvanceToNextPartition (i, Partition, Batchable.size ());
782
+ std::vector<const Job*> &P = Partition[i];
783
+ if (P.empty () || TC.jobsAreBatchCombinable (Comp, P[0 ], Cmd)) {
784
+ if (Comp.ShowJobLifecycle )
785
+ llvm::outs () << " Adding " << LogJob (Cmd)
786
+ << " to batch " << i << ' \n ' ;
787
+ P.push_back (Cmd);
788
+ } else {
789
+ // Strange but theoretically possible that we have a batchable job
790
+ // that's not combinable with others; tack a new batch on for it.
791
+ if (Comp.ShowJobLifecycle )
792
+ llvm::outs () << " Adding " << LogJob (Cmd)
793
+ << " to new batch " << Partition.size () << ' \n ' ;
794
+ Partition.push_back (std::vector<const Job*>());
795
+ Partition.back ().push_back (Cmd);
796
+ }
753
797
}
754
- llvm::outs () << " Adding to batch: " << LogJob (Cmd) << " \n " ;
755
- CurrentBatch.insert (Cmd);
756
798
}
757
799
758
800
// / Select jobs that are batch-combinable from \c PendingExecution, combine
@@ -768,25 +810,18 @@ namespace driver {
768
810
return ;
769
811
}
770
812
771
- // Partition the pending jobs.
772
- CommandSet Batchable, NonBatchable, Batches ;
813
+ // Split the batchable from non-batchable pending jobs.
814
+ CommandSetVector Batchable, NonBatchable;
773
815
getPendingBatchableJobs (Batchable, NonBatchable);
774
- size_t TargetBatchSize = Batchable.size () / Comp.NumberOfParallelCommands ;
775
816
776
- if (Comp.ShowJobLifecycle ) {
777
- llvm::outs () << " Found " << Batchable.size () << " batchable jobs\n " ;
778
- llvm::outs () << " Aiming for batch size " << TargetBatchSize << ' \n ' ;
779
- }
780
-
781
- // Batch the batchable jobs.
782
- llvm::SetVector<const Job *> CurrentBatch;
783
- for (const Job *Cmd : Batchable) {
784
- expandBatch (Cmd, Batches, CurrentBatch, TargetBatchSize);
785
- }
817
+ // Partition the batchable jobs into sets.
818
+ BatchPartition Partition (Comp.NumberOfParallelCommands );
819
+ partitionIntoBatches (Batchable.takeVector (), Partition);
786
820
787
- // Form a residual incomplete batch if any jobs remain.
788
- if (!CurrentBatch.empty ()) {
789
- formBatchJobFromCurrentBatch (Batches, CurrentBatch);
821
+ // Construct a BatchJob from each batch in the partition.
822
+ std::vector<const Job *> Batches;
823
+ for (auto const &Batch : Partition) {
824
+ formBatchJobFromPartitionBatch (Batches, Batch);
790
825
}
791
826
792
827
// Save batches so we can locate and decompose them on task-exit.
0 commit comments