1*760c253cSXin Li# Copyright 2013 The ChromiumOS Authors 2*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 3*760c253cSXin Li# found in the LICENSE file. 4*760c253cSXin Li"""Iterative flags elimination. 5*760c253cSXin Li 6*760c253cSXin LiPart of the Chrome build flags optimization. 7*760c253cSXin Li 8*760c253cSXin LiThis module implements the flag iterative elimination algorithm (IE) adopted 9*760c253cSXin Lifrom the paper 10*760c253cSXin LiZ. Pan et al. Fast and Effective Orchestration of Compiler Optimizations for 11*760c253cSXin LiAutomatic Performance Tuning. 12*760c253cSXin Li 13*760c253cSXin LiIE begins with the base line that turns on all the optimizations flags and 14*760c253cSXin Lisetting the numeric flags to their highest values. IE turns off the one boolean 15*760c253cSXin Liflag or lower the value of a numeric flag with the most negative effect from the 16*760c253cSXin Libaseline. This process repeats with all remaining flags, until none of them 17*760c253cSXin Licauses performance degradation. The complexity of IE is O(n^2). 18*760c253cSXin Li 19*760c253cSXin LiFor example, -fstrict-aliasing and -ftree-vectorize. The base line is 20*760c253cSXin Lib=[-fstrict-aliasing, -ftree-vectorize]. The two tasks in the first iteration 21*760c253cSXin Liare t0=[-fstrict-aliasing] and t1=[-ftree-vectorize]. The algorithm compares b 22*760c253cSXin Liwith t0 and t1, respectively, and see whether setting the numeric flag with a 23*760c253cSXin Lilower value or removing the boolean flag -fstrict-aliasing produce a better 24*760c253cSXin Lifitness value. 25*760c253cSXin Li""" 26*760c253cSXin Li 27*760c253cSXin Li__author__ = "[email protected] (Yuheng Long)" 28*760c253cSXin Li 29*760c253cSXin Liimport flags 30*760c253cSXin Lifrom generation import Generation 31*760c253cSXin Liimport task 32*760c253cSXin Li 33*760c253cSXin Li 34*760c253cSXin Lidef _DecreaseFlag(flags_dict, spec): 35*760c253cSXin Li """Decrease the value of the flag that has the specification spec. 36*760c253cSXin Li 37*760c253cSXin Li If the flag that contains the spec is a boolean flag, it is eliminated. 38*760c253cSXin Li Otherwise the flag is a numeric flag, its value will be reduced by one. 39*760c253cSXin Li 40*760c253cSXin Li Args: 41*760c253cSXin Li flags_dict: The dictionary containing the original flags whose neighbors are 42*760c253cSXin Li to be explored. 43*760c253cSXin Li spec: The spec in the flags_dict is to be changed. 44*760c253cSXin Li 45*760c253cSXin Li Returns: 46*760c253cSXin Li Dictionary of neighbor flag that is only different from the original 47*760c253cSXin Li dictionary by the spec. 48*760c253cSXin Li """ 49*760c253cSXin Li 50*760c253cSXin Li # The specification must be held by one of the flags. 51*760c253cSXin Li assert spec in flags_dict 52*760c253cSXin Li 53*760c253cSXin Li # The results this method returns. 54*760c253cSXin Li results = flags_dict.copy() 55*760c253cSXin Li 56*760c253cSXin Li # This method searches for a pattern [start-end] in the spec. If the spec 57*760c253cSXin Li # contains this pattern, it is a numeric flag. Otherwise it is a boolean flag. 58*760c253cSXin Li # For example, -finline-limit=[1-1000] is a numeric flag and -falign-jumps is 59*760c253cSXin Li # a boolean flag. 60*760c253cSXin Li numeric_flag_match = flags.Search(spec) 61*760c253cSXin Li 62*760c253cSXin Li if numeric_flag_match: 63*760c253cSXin Li # numeric flag 64*760c253cSXin Li val = results[spec].GetValue() 65*760c253cSXin Li 66*760c253cSXin Li # If the value of the flag is the lower boundary of the specification, this 67*760c253cSXin Li # flag will be turned off. Because it already contains the lowest value and 68*760c253cSXin Li # can not be decreased any more. 69*760c253cSXin Li if val == int(numeric_flag_match.group("start")): 70*760c253cSXin Li # Turn off the flag. A flag is turned off if it is not presented in the 71*760c253cSXin Li # flags_dict. 72*760c253cSXin Li del results[spec] 73*760c253cSXin Li else: 74*760c253cSXin Li results[spec] = flags.Flag(spec, val - 1) 75*760c253cSXin Li else: 76*760c253cSXin Li # Turn off the flag. A flag is turned off if it is not presented in the 77*760c253cSXin Li # flags_dict. 78*760c253cSXin Li del results[spec] 79*760c253cSXin Li 80*760c253cSXin Li return results 81*760c253cSXin Li 82*760c253cSXin Li 83*760c253cSXin Liclass IterativeEliminationGeneration(Generation): 84*760c253cSXin Li """The negative flag iterative elimination algorithm.""" 85*760c253cSXin Li 86*760c253cSXin Li def __init__(self, exe_set, parent_task): 87*760c253cSXin Li """Set up the base line parent task. 88*760c253cSXin Li 89*760c253cSXin Li The parent task is the base line against which the new tasks are compared. 90*760c253cSXin Li The new tasks are only different from the base line from one flag f by 91*760c253cSXin Li either turning this flag f off, or lower the flag value by 1. 92*760c253cSXin Li If a new task is better than the base line, one flag is identified that 93*760c253cSXin Li gives degradation. The flag that give the worst degradation will be removed 94*760c253cSXin Li or lower the value by 1 in the base in each iteration. 95*760c253cSXin Li 96*760c253cSXin Li Args: 97*760c253cSXin Li exe_set: A set of tasks to be run. Each one only differs from the 98*760c253cSXin Li parent_task by one flag. 99*760c253cSXin Li parent_task: The base line task, against which the new tasks in exe_set 100*760c253cSXin Li are compared. 101*760c253cSXin Li """ 102*760c253cSXin Li 103*760c253cSXin Li Generation.__init__(self, exe_set, None) 104*760c253cSXin Li self._parent_task = parent_task 105*760c253cSXin Li 106*760c253cSXin Li def IsImproved(self): 107*760c253cSXin Li """Whether any new task has improvement upon the parent task.""" 108*760c253cSXin Li 109*760c253cSXin Li parent = self._parent_task 110*760c253cSXin Li # Whether there is any new task that has improvement over the parent base 111*760c253cSXin Li # line task. 112*760c253cSXin Li for curr in [curr for curr in self.Pool() if curr != parent]: 113*760c253cSXin Li if curr.IsImproved(parent): 114*760c253cSXin Li return True 115*760c253cSXin Li 116*760c253cSXin Li return False 117*760c253cSXin Li 118*760c253cSXin Li def Next(self, cache): 119*760c253cSXin Li """Find out the flag that gives the worst degradation. 120*760c253cSXin Li 121*760c253cSXin Li Found out the flag that gives the worst degradation. Turn that flag off from 122*760c253cSXin Li the base line and use the new base line for the new generation. 123*760c253cSXin Li 124*760c253cSXin Li Args: 125*760c253cSXin Li cache: A set of tasks that have been generated before. 126*760c253cSXin Li 127*760c253cSXin Li Returns: 128*760c253cSXin Li A set of new generations. 129*760c253cSXin Li """ 130*760c253cSXin Li parent_task = self._parent_task 131*760c253cSXin Li 132*760c253cSXin Li # Find out the task that gives the worst degradation. 133*760c253cSXin Li worst_task = parent_task 134*760c253cSXin Li 135*760c253cSXin Li for curr in [curr for curr in self.Pool() if curr != parent_task]: 136*760c253cSXin Li # The method IsImproved, which is supposed to be called before, ensures 137*760c253cSXin Li # that there is at least a task that improves upon the parent_task. 138*760c253cSXin Li if curr.IsImproved(worst_task): 139*760c253cSXin Li worst_task = curr 140*760c253cSXin Li 141*760c253cSXin Li assert worst_task != parent_task 142*760c253cSXin Li 143*760c253cSXin Li # The flags_set of the worst task. 144*760c253cSXin Li work_flags_set = worst_task.GetFlags().GetFlags() 145*760c253cSXin Li 146*760c253cSXin Li results = set([]) 147*760c253cSXin Li 148*760c253cSXin Li # If the flags_set contains no flag, i.e., all the flags have been 149*760c253cSXin Li # eliminated, the algorithm stops. 150*760c253cSXin Li if not work_flags_set: 151*760c253cSXin Li return [] 152*760c253cSXin Li 153*760c253cSXin Li # Turn of the remaining flags one by one for the next generation. 154*760c253cSXin Li for spec in work_flags_set: 155*760c253cSXin Li flag_set = flags.FlagSet( 156*760c253cSXin Li _DecreaseFlag(work_flags_set, spec).values() 157*760c253cSXin Li ) 158*760c253cSXin Li new_task = task.Task(flag_set) 159*760c253cSXin Li if new_task not in cache: 160*760c253cSXin Li results.add(new_task) 161*760c253cSXin Li 162*760c253cSXin Li return [IterativeEliminationGeneration(results, worst_task)] 163*760c253cSXin Li 164*760c253cSXin Li 165*760c253cSXin Liclass IterativeEliminationFirstGeneration(IterativeEliminationGeneration): 166*760c253cSXin Li """The first iteration of the iterative elimination algorithm. 167*760c253cSXin Li 168*760c253cSXin Li The first iteration also evaluates the base line task. The base line tasks in 169*760c253cSXin Li the subsequent iterations have been evaluated. Therefore, 170*760c253cSXin Li IterativeEliminationGeneration does not include the base line task in the 171*760c253cSXin Li execution set. 172*760c253cSXin Li """ 173*760c253cSXin Li 174*760c253cSXin Li def IsImproved(self): 175*760c253cSXin Li # Find out the base line task in the execution set. 176*760c253cSXin Li parent = next(task for task in self.Pool() if task == self._parent_task) 177*760c253cSXin Li self._parent_task = parent 178*760c253cSXin Li 179*760c253cSXin Li return IterativeEliminationGeneration.IsImproved(self) 180