Skip to content

Commit cf09641

Browse files
committed
Fix critical time calculation
The existing algorithm doesn't work because it strictly requires that all outputs are visited before updating an edge. So any task downstream from a task with multiple out-edges may get ignored. The fix is to always propagate your critical time to the next input node, and only place it in the queue if you offer a higher critical time.
1 parent 8e23200 commit cf09641

File tree

2 files changed

+37
-70
lines changed

2 files changed

+37
-70
lines changed

src/build.cc

Lines changed: 33 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ bool DryRunCommandRunner::WaitForCommand(Result* result) {
7777

7878

7979
bool EdgeQueue::EdgePriorityCompare::operator()(const Edge* e1, const Edge* e2) const {
80-
const uint64_t ct1 = e1->critical_time();
81-
const uint64_t ct2 = e2->critical_time();
80+
const int64_t ct1 = e1->critical_time();
81+
const int64_t ct2 = e2->critical_time();
8282
if (ct1 != ct2) {
8383
return ct1 < ct2;
8484
}
@@ -464,28 +464,10 @@ void Plan::ComputeCriticalTime(BuildLog* build_log) {
464464
targets_.end());
465465
}
466466

467-
468-
std::vector<Edge*> edges;
469-
std::map<Node*, int> num_out_edges;
470-
for (std::map<Edge*, Want>::iterator it = want_.begin(), end = want_.end();
471-
it != end; ++it) {
472-
if (it->second != kWantNothing)
473-
continue;
474-
Edge* e = it->first;
475-
edges.push_back(e);
476-
std::set<Node*> ins; // protect against #308; also sanity
477-
for (size_t nit = 0; nit < e->inputs_.size(); ++nit) {
478-
Node* n = e->inputs_[nit];
479-
if (ins.insert(n).second) {
480-
num_out_edges[n]++;
481-
}
482-
}
483-
}
484-
485467
// this is total time if building all edges in serial, so this value is big
486468
// enough to ensure higher priority target initial critical time always bigger
487469
// than lower one
488-
uint64_t total_time = 0;
470+
int64_t total_time = 0;
489471
// Critical path scheduling.
490472
// 0. Assign costs to all edges, using:
491473
// a) The time the edge needed last time, if available.
@@ -496,11 +478,12 @@ void Plan::ComputeCriticalTime(BuildLog* build_log) {
496478
// c) A fixed cost if this type of edge hasn't run before (0 for phony target,
497479
// 1 for others)
498480
//
499-
for (std::vector<Edge*>::iterator it = edges.begin(), end = edges.end(); it != end;
500-
total_time += (*it)->run_time_ms_, ++it) {
501-
Edge* edge = *it;
502-
if (edge->is_phony())
481+
for (std::map<Edge*, Want>::iterator it = want_.begin(), end = want_.end();
482+
it != end; ++it) {
483+
Edge* edge = it->first;
484+
if (edge->is_phony()) {
503485
continue;
486+
}
504487
BuildLog::LogEntry* entry =
505488
build_log->LookupByOutput(edge->outputs_[0]->path());
506489
if (!entry) {
@@ -512,63 +495,47 @@ void Plan::ComputeCriticalTime(BuildLog* build_log) {
512495
}
513496

514497
// Use backflow algorithm to compute critical times for all nodes, starting
515-
// from the destination nodes. use priority_weight = total_time * N as
516-
// initial critical time to makes forward edgs of higher priority always
517-
// get higher critical time value
498+
// from the destination nodes.
518499
// XXX: ignores pools
519-
std::queue<Edge*> edgesQ;
520-
521-
// Makes sure that each edge is added to the queue just once. This is needed
522-
// for example if a binary is used to generate 50 source files, and all the
523-
// source file cxx lines are added. Without this, the edge generating that
524-
// binary would be added ot the queue 50 times.
525-
std::set<Edge*> done;
500+
std::set<Edge*> active_edges; // All edges in edgesQ (for uniqueness)
501+
std::queue<Edge*> edgesQ; // Queue, for breadth-first traversal
526502

527503
for (std::vector<const Node*>::reverse_iterator it = targets_.rbegin(),
528504
end = targets_.rend();
529505
it != end; ++it) {
530506
if (Edge* in = (*it)->in_edge()) {
531-
uint64_t priority_weight = (it - targets_.rbegin()) * total_time;
507+
// Use initial critical time: total_time * N. This means higher
508+
// priority targets always get a higher critical time value
509+
int64_t priority_weight = (it - targets_.rbegin()) * total_time;
532510
in->set_critical_time(
533-
std::max(std::max<uint64_t>(in->run_time_ms_, priority_weight),
534-
in->critical_time()));
535-
if (done.insert(in).second) {
511+
priority_weight +
512+
std::max<int64_t>(in->run_time_ms_, in->critical_time()));
513+
if (active_edges.insert(in).second) {
536514
edgesQ.push(in);
515+
all_edges.insert(in);
537516
}
538517
}
539518
}
519+
540520
while (!edgesQ.empty()) {
541-
Edge* e = edgesQ.front(); edgesQ.pop();
542-
bool all_nodes_ready = true;
543-
uint64_t max_crit = 0;
544-
for (std::vector<Node*>::iterator it = e->outputs_.begin(),
545-
end = e->outputs_.end();
546-
it != end; ++it) {
547-
if (num_out_edges[*it] > 0) {
548-
all_nodes_ready = false;
549-
continue;
550-
}
551-
for (std::vector<Edge*>::const_iterator eit = (*it)->out_edges().begin(),
552-
eend = (*it)->out_edges().end();
553-
eit != eend; ++eit) {
554-
max_crit = std::max((*eit)->critical_time(), max_crit);
555-
}
556-
}
557-
if (!all_nodes_ready) {
558-
// To the back it goes.
559-
// XXX: think about how often this can happen.
560-
edgesQ.push(e);
561-
continue;
562-
}
563-
e->set_critical_time(std::max(max_crit + e->run_time_ms_, e->critical_time()));
521+
Edge* e = edgesQ.front();
522+
edgesQ.pop();
523+
active_edges.erase(e);
564524

565525
for (std::vector<Node*>::iterator it = e->inputs_.begin(),
566526
end = e->inputs_.end();
567527
it != end; ++it) {
568-
num_out_edges[*it]--;
569-
if (Edge* in = (*it)->in_edge()) {
570-
if (done.insert(in).second) {
528+
Edge* in = (*it)->in_edge();
529+
if (!in) {
530+
continue;
531+
}
532+
// Only process edge if this node offers a higher critical time
533+
const int64_t proposed_time = e->critical_time() + in->run_time_ms_;
534+
if (proposed_time > in->critical_time()) {
535+
in->set_critical_time(proposed_time);
536+
if (active_edges.insert(in).second) {
571537
edgesQ.push(in);
538+
all_edges.insert(in);
572539
}
573540
}
574541
}
@@ -725,7 +692,7 @@ bool Builder::AlreadyUpToDate() const {
725692
bool Builder::Build(string* err) {
726693
assert(!AlreadyUpToDate());
727694

728-
plan_.ComputePriorityList(scan_.build_log());
695+
plan_.ComputeCriticalTime(scan_.build_log());
729696

730697
status_->PlanHasTotalEdges(plan_.command_edge_count());
731698
int pending_commands = 0;

src/graph.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ struct Edge {
146146

147147
Edge()
148148
: rule_(NULL), pool_(NULL), dyndep_(NULL), env_(NULL), mark_(VisitNone),
149-
id_(0), run_time_ms_(0), critical_time_(0), outputs_ready_(false),
149+
id_(0), run_time_ms_(0), critical_time_(-1), outputs_ready_(false),
150150
deps_loaded_(false), deps_missing_(false),
151151
generated_by_dep_loader_(false), implicit_deps_(0),
152152
order_only_deps_(0), implicit_outs_(0) {}
@@ -172,8 +172,8 @@ struct Edge {
172172

173173
void Dump(const char* prefix="") const;
174174

175-
uint64_t critical_time() const { return critical_time_; }
176-
void set_critical_time(uint64_t critical_time) { critical_time_ = critical_time; }
175+
int64_t critical_time() const { return critical_time_; }
176+
void set_critical_time(int64_t critical_time) { critical_time_ = critical_time; }
177177

178178
const Rule* rule_;
179179
Pool* pool_;
@@ -184,7 +184,7 @@ struct Edge {
184184
VisitMark mark_;
185185
size_t id_;
186186
int run_time_ms_;
187-
uint64_t critical_time_;
187+
int64_t critical_time_;
188188
bool outputs_ready_;
189189
bool deps_loaded_;
190190
bool deps_missing_;

0 commit comments

Comments
 (0)