108 lines
3.3 KiB
Python
108 lines
3.3 KiB
Python
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
"""A variation of the hill climbing algorithm.
|
|
|
|
Part of the Chrome build flags optimization.
|
|
|
|
This algorithm explores all the neighbors of the current task. If at least one
|
|
neighbor gives better performance than the current task, it explores the best
|
|
neighbor.
|
|
"""
|
|
|
|
__author__ = 'yuhenglong@google.com (Yuheng Long)'
|
|
|
|
from flags import FlagSet
|
|
import flags_util
|
|
from generation import Generation
|
|
from task import Task
|
|
|
|
|
|
class HillClimbingBestBranch(Generation):
|
|
"""A variation of the hill climbing algorithm.
|
|
|
|
Given a task, it explores all its neighbors. Pick the best neighbor for the
|
|
next iteration.
|
|
"""
|
|
|
|
def __init__(self, exe_set, parents, specs):
|
|
"""Set up the tasks set of this generation.
|
|
|
|
Args:
|
|
exe_set: A set of tasks to be run.
|
|
parents: A set of tasks to be used to check whether their neighbors have
|
|
improved upon them.
|
|
specs: A list of specs to explore. The spec specifies the flags that can
|
|
be changed to find neighbors of a task.
|
|
"""
|
|
|
|
Generation.__init__(self, exe_set, parents)
|
|
self._specs = specs
|
|
|
|
# This variable will be used, by the Next method, to generate the tasks for
|
|
# the next iteration. This self._next_task contains the best task in the
|
|
# current iteration and it will be set by the IsImproved method. The tasks
|
|
# of the next iteration are the neighbor of self._next_task.
|
|
self._next_task = None
|
|
|
|
def IsImproved(self):
|
|
"""True if this generation has improvement over its parent generation.
|
|
|
|
If this generation improves upon the previous generation, this method finds
|
|
out the best task in this generation and sets it to _next_task for the
|
|
method Next to use.
|
|
|
|
Returns:
|
|
True if the best neighbor improves upon the parent task.
|
|
"""
|
|
|
|
# Find the best neighbor.
|
|
best_task = None
|
|
for task in self._exe_set:
|
|
if not best_task or task.IsImproved(best_task):
|
|
best_task = task
|
|
|
|
if not best_task:
|
|
return False
|
|
|
|
# The first generation may not have parent generation.
|
|
parents = list(self._candidate_pool)
|
|
if parents:
|
|
assert len(parents) == 1
|
|
self._next_task = best_task
|
|
# If the best neighbor improves upon the parent task.
|
|
return best_task.IsImproved(parents[0])
|
|
|
|
self._next_task = best_task
|
|
return True
|
|
|
|
def Next(self, cache):
|
|
"""Calculate the next generation.
|
|
|
|
The best neighbor b of the current task is the parent of the next
|
|
generation. The neighbors of b will be the set of tasks to be evaluated
|
|
next.
|
|
|
|
Args:
|
|
cache: A set of tasks that have been generated before.
|
|
|
|
Returns:
|
|
A set of new generations.
|
|
"""
|
|
|
|
# The best neighbor.
|
|
current_task = self._next_task
|
|
flag_set = current_task.GetFlags()
|
|
|
|
# The neighbors of the best neighbor.
|
|
children_tasks = set([])
|
|
for spec in self._specs:
|
|
for next_flag in flags_util.ClimbNext(flag_set.GetFlags(), spec):
|
|
new_task = Task(FlagSet(next_flag.values()))
|
|
|
|
if new_task not in cache:
|
|
children_tasks.add(new_task)
|
|
|
|
return [HillClimbingBestBranch(children_tasks, set([current_task]),
|
|
self._specs)]
|