From 8491c47076d283b0914575ba6a38d3908de61844 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Fri, 18 May 2018 09:58:21 +0200 Subject: [PATCH] run_graph: skip already executed tasks while walking the graph This has the nice side-effect of skipping the prepare step of crates already fully tested. --- src/run_graph.rs | 28 ++++++++++++++++++++++------ src/tasks.rs | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/run_graph.rs b/src/run_graph.rs index 653228cd4..d695bdf8f 100644 --- a/src/run_graph.rs +++ b/src/run_graph.rs @@ -70,23 +70,39 @@ impl TasksGraph { id } - pub fn next_task(&mut self) -> Option<(NodeIndex, Task)> { + pub fn next_task( + &mut self, + ex: &Experiment, + db: &DB, + ) -> Option<(NodeIndex, Task)> { let root = self.root; - if let WalkResult::Task(id, task) = self.walk_graph(root) { + if let WalkResult::Task(id, task) = self.walk_graph(root, ex, db) { Some((id, task)) } else { None } } - fn walk_graph(&mut self, node: NodeIndex) -> WalkResult { + fn walk_graph( + &mut self, + node: NodeIndex, + ex: &Experiment, + db: &DB, + ) -> WalkResult { + // Ensure tasks are only executed if needed + if let Node::Task(ref task) = self.graph[node] { + if !task.needs_exec(ex, db) { + return WalkResult::NotBlocked; + } + } + let mut dependencies = 0; // Try to check for the dependencies of this node // The list is collected to make the borrowchecker happy let mut neighbors = self.graph.neighbors(node).collect::>(); for neighbor in neighbors.drain(..) { - match self.walk_graph(neighbor) { + match self.walk_graph(neighbor, ex, db) { WalkResult::Task(id, task) => return WalkResult::Task(id, task), WalkResult::Blocked => dependencies += 1, WalkResult::NotBlocked => {} @@ -197,7 +213,7 @@ pub fn run_ex( let join = scope.builder().name(name).spawn(|| -> Result<()> { // This uses a `loop` instead of a `while let` to avoid locking the graph too much loop { - let option_task = graph.lock().unwrap().next_task(); + let option_task = graph.lock().unwrap().next_task(ex, db); if let Some((id, task)) = option_task { info!("running task: {:?}", task); task.run(ex, db)?; @@ -221,7 +237,7 @@ pub fn run_ex( // Only the root node must be present let mut g = graph.lock().unwrap(); - assert!(g.next_task().is_none()); + assert!(g.next_task(ex, db).is_none()); assert_eq!(g.graph.neighbors(g.root).count(), 0); Ok(()) diff --git a/src/tasks.rs b/src/tasks.rs index fcca9f5d6..bea617210 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -58,6 +58,20 @@ impl fmt::Debug for Task { } impl Task { + pub fn needs_exec(&self, ex: &Experiment, db: &DB) -> bool { + // A prepare step should already be executed, and other steps only if were not executed + // already (on error checking if the step was executed it's executed again just to be safe) + match self.step { + TaskStep::Prepare => true, + TaskStep::BuildAndTest { ref tc, .. } + | TaskStep::BuildOnly { ref tc, .. } + | TaskStep::CheckOnly { ref tc, .. } + | TaskStep::UnstableFeatures { ref tc } => db.already_executed(ex, tc, &self.krate) + .unwrap_or(None) + .is_none(), + } + } + pub fn run(&self, ex: &Experiment, db: &DB) -> Result<()> { match self.step { TaskStep::Prepare => self.run_prepare(ex, db),