xref: /aosp_15_r20/external/toolchain-utils/bestflags/iterative_elimination.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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