hill_climb_best_neighbor.py revision c01232264caccaffe6302414b7e901a96d3b18bf
1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""A variation of the hill climbing algorithm.
6
7Part of the Chrome build flags optimization.
8
9This algorithm explores all the neighbors of the current task. If at least one
10neighbor gives better performance than the current task, it explores the best
11neighbor.
12"""
13
14__author__ = 'yuhenglong@google.com (Yuheng Long)'
15
16
17from flags import FlagSet
18import flags_util
19from generation import Generation
20from task import Task
21
22
23class HillClimbingBestBranch(Generation):
24  """A variation of the hill climbing algorithm.
25
26  Given a task, it explores all its neighbors. Pick the best neighbor for the
27  next iteration.
28  """
29
30  def __init__(self, exe_set, parents, specs):
31    """Set up the tasks set of this generation.
32
33    Args:
34      exe_set: A set of tasks to be run.
35      parents: A set of tasks to be used to check whether their neighbors have
36        improved upon them.
37      specs: A list of specs to explore. The spec specifies the flags that can
38        be changed to find neighbors of a task.
39    """
40
41    Generation.__init__(self, exe_set, parents)
42    self._specs = specs
43
44  def IsImproved(self):
45    """True if this generation has improvement over its parent generation.
46
47    If this generation improves upon the previous generation, this method finds
48    out the best task in this generation and sets it to _next_task for the
49    method Next to use.
50
51    Returns:
52      True if the best neighbor improves upon the parent task.
53    """
54
55    # Find the best neighbor.
56    best_task = None
57    for task in self._exe_set:
58      if not best_task or task.IsImproved(best_task):
59        best_task = task
60
61    if not best_task:
62      return False
63
64    # The first generation may not have parent generation.
65    parents = list(self._candidate_pool)
66    if parents:
67      assert len(parents) == 1
68      self._next_task = best_task
69      # If the best neighbor improves upon the parent task.
70      return best_task.IsImproved(parents[0])
71
72    self._next_task = best_task
73    return True
74
75  def Next(self, cache):
76    """Calculate the next generation.
77
78    The best neighbor b of the current task is the parent of the next
79    generation. The neighbors of b will be the set of tasks to be evaluated
80    next.
81
82    Args:
83      cache: A set of tasks that have been generated before.
84
85    Returns:
86      A set of new generations.
87    """
88
89    # The best neighbor.
90    current_task = self._next_task
91    flag_set = current_task.GetFlags()
92
93    # The neighbors of the best neighbor.
94    children_tasks = set([])
95    for spec in self._specs:
96      for next_flag in flags_util.ClimbNext(flag_set.GetFlags(), spec):
97        new_task = Task(FlagSet(next_flag.values()))
98
99        if new_task not in cache:
100          children_tasks.add(new_task)
101
102    return [HillClimbingBestBranch(children_tasks, set([current_task]),
103                                   self._specs)]
104