-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Add critical path scheduler to improve build times #2019
Changes from 5 commits
4af9fc5
12b5b7c
8e23200
2fcf403
c5d355c
63b0a9a
5b8d19b
fe80637
c83167f
77448b4
24d1f5f
1af6daf
6ee9049
1128a56
a861164
026498f
4bd8db1
a643af2
f2333b7
09d4faa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,16 @@ bool DryRunCommandRunner::WaitForCommand(Result* result) { | |
|
||
} // namespace | ||
|
||
|
||
bool EdgeQueue::EdgePriorityCompare::operator()(const Edge* e1, const Edge* e2) const { | ||
const int64_t ct1 = e1->critical_time(); | ||
const int64_t ct2 = e2->critical_time(); | ||
if (ct1 != ct2) { | ||
return ct1 < ct2; | ||
} | ||
return e1->id_ < e2->id_; | ||
} | ||
|
||
Plan::Plan(Builder* builder) | ||
: builder_(builder) | ||
, command_edges_(0) | ||
|
@@ -89,6 +99,7 @@ void Plan::Reset() { | |
} | ||
|
||
bool Plan::AddTarget(const Node* target, string* err) { | ||
targets_.push_back(target); | ||
return AddSubTarget(target, NULL, err, NULL); | ||
} | ||
|
||
|
@@ -151,10 +162,7 @@ void Plan::EdgeWanted(const Edge* edge) { | |
Edge* Plan::FindWork() { | ||
if (ready_.empty()) | ||
return NULL; | ||
EdgeSet::iterator e = ready_.begin(); | ||
Edge* edge = *e; | ||
ready_.erase(e); | ||
return edge; | ||
return ready_.pop(); | ||
} | ||
|
||
void Plan::ScheduleWork(map<Edge*, Want>::iterator want_e) { | ||
|
@@ -172,10 +180,12 @@ void Plan::ScheduleWork(map<Edge*, Want>::iterator want_e) { | |
Pool* pool = edge->pool(); | ||
if (pool->ShouldDelayEdge()) { | ||
pool->DelayEdge(edge); | ||
pool->RetrieveReadyEdges(&ready_); | ||
EdgeSet new_edges; | ||
pool->RetrieveReadyEdges(&new_edges); | ||
ready_.push(new_edges.begin(), new_edges.end()); | ||
} else { | ||
pool->EdgeScheduled(*edge); | ||
ready_.insert(edge); | ||
ready_.push(edge); | ||
} | ||
} | ||
|
||
|
@@ -187,7 +197,9 @@ bool Plan::EdgeFinished(Edge* edge, EdgeResult result, string* err) { | |
// See if this job frees up any delayed jobs. | ||
if (directly_wanted) | ||
edge->pool()->EdgeFinished(*edge); | ||
edge->pool()->RetrieveReadyEdges(&ready_); | ||
EdgeSet new_edges; | ||
edge->pool()->RetrieveReadyEdges(&new_edges); | ||
ready_.push(new_edges.begin(), new_edges.end()); | ||
|
||
// The rest of this function only applies to successful commands. | ||
if (result != kEdgeSucceeded) | ||
|
@@ -424,6 +436,109 @@ void Plan::UnmarkDependents(const Node* node, set<Node*>* dependents) { | |
} | ||
} | ||
|
||
namespace { | ||
|
||
struct SeenNodeBefore { | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
std::set<const Node*> *seen; | ||
jhasse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
bool operator() (const Node* node) { | ||
// Return true if the node has been seen before | ||
return !seen->insert(node).second; | ||
} | ||
}; | ||
|
||
} // namespace | ||
|
||
void Plan::ComputeCriticalTime(BuildLog* build_log) { | ||
// testcases have no build_log | ||
if (!build_log) | ||
return; | ||
|
||
METRIC_RECORD("ComputePriorityList"); | ||
// Remove duplicate targets | ||
{ | ||
std::set<const Node*> seen; | ||
targets_.erase(std::remove_if(targets_.begin(), targets_.end(), | ||
SeenNodeBefore{ &seen }), | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
targets_.end()); | ||
} | ||
|
||
// this is total time if building all edges in serial, so this value is big | ||
// enough to ensure higher priority target initial critical time always bigger | ||
// than lower one | ||
int64_t total_time = 0; | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Critical path scheduling. | ||
// 0. Assign costs to all edges, using: | ||
// a) The time the edge needed last time, if available. | ||
// b) The average time this edge type needed, if this edge hasn't run before. | ||
// (not implemented .log entries is not grouped by rule type, and even | ||
jhasse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// similar rule type may not have same name , for example two compile rule | ||
// with different compile flags) | ||
// c) A fixed cost if this type of edge hasn't run before (0 for phony target, | ||
// 1 for others) | ||
// | ||
for (std::map<Edge*, Want>::iterator it = want_.begin(), end = want_.end(); | ||
it != end; ++it) { | ||
Edge* edge = it->first; | ||
if (edge->is_phony()) { | ||
continue; | ||
} | ||
BuildLog::LogEntry* entry = | ||
build_log->LookupByOutput(edge->outputs_[0]->path()); | ||
if (!entry) { | ||
edge->run_time_ms_ = 1; | ||
continue; | ||
} | ||
int duration = entry->end_time - entry->start_time; | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
edge->run_time_ms_ = duration; | ||
} | ||
|
||
// Use backflow algorithm to compute critical times for all nodes, starting | ||
// from the destination nodes. | ||
// XXX: ignores pools | ||
std::set<Edge*> active_edges; // All edges in edgesQ (for uniqueness) | ||
std::queue<Edge*> edgesQ; // Queue, for breadth-first traversal | ||
|
||
for (std::vector<const Node*>::reverse_iterator it = targets_.rbegin(), | ||
end = targets_.rend(); | ||
it != end; ++it) { | ||
if (Edge* in = (*it)->in_edge()) { | ||
// Use initial critical time: total_time * N. This means higher | ||
// priority targets always get a higher critical time value | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int64_t priority_weight = (it - targets_.rbegin()) * total_time; | ||
in->set_critical_time( | ||
priority_weight + | ||
std::max<int64_t>(in->run_time_ms_, in->critical_time())); | ||
if (active_edges.insert(in).second) { | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
edgesQ.push(in); | ||
} | ||
} | ||
} | ||
|
||
while (!edgesQ.empty()) { | ||
Edge* e = edgesQ.front(); | ||
edgesQ.pop(); | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
active_edges.erase(e); | ||
peterbell10 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for (std::vector<Node*>::iterator it = e->inputs_.begin(), | ||
end = e->inputs_.end(); | ||
This comment was marked as abuse.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assumed C++11 isn't required yet from the code style used elsewhere. Is that correct?
This comment was marked as abuse.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also means no |
||
it != end; ++it) { | ||
Edge* in = (*it)->in_edge(); | ||
if (!in) { | ||
continue; | ||
} | ||
// Only process edge if this node offers a higher critical time | ||
const int64_t proposed_time = e->critical_time() + in->run_time_ms_; | ||
if (proposed_time > in->critical_time()) { | ||
in->set_critical_time(proposed_time); | ||
if (active_edges.insert(in).second) { | ||
edgesQ.push(in); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
void Plan::Dump() const { | ||
printf("pending: %d\n", (int)want_.size()); | ||
for (map<Edge*, Want>::const_iterator e = want_.begin(); e != want_.end(); ++e) { | ||
|
@@ -574,6 +689,8 @@ bool Builder::AlreadyUpToDate() const { | |
bool Builder::Build(string* err) { | ||
assert(!AlreadyUpToDate()); | ||
|
||
plan_.ComputeCriticalTime(scan_.build_log()); | ||
|
||
status_->PlanHasTotalEdges(plan_.command_edge_count()); | ||
int pending_commands = 0; | ||
int failures_allowed = config_.failures_allowed; | ||
|
This comment was marked as abuse.
Sorry, something went wrong.
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.
graph.h
which definesEdge
isn't included in the header.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.
You could template it. (And if you really mean for it to be just
Edge*
, you can static_assert that the template is a forward-declared type. As in