17d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# SPDX-License-Identifier: Apache-2.0
27d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman#
37d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# Copyright (C) 2016, ARM Limited and contributors.
47d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman#
57d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# Licensed under the Apache License, Version 2.0 (the "License"); you may
67d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# not use this file except in compliance with the License.
77d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# You may obtain a copy of the License at
87d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman#
97d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# http://www.apache.org/licenses/LICENSE-2.0
107d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman#
117d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# Unless required by applicable law or agreed to in writing, software
127d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
137d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
147d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# See the License for the specific language governing permissions and
157d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman# limitations under the License.
167d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman#
177d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
187d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanfrom math import isnan
197d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
207d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanimport numpy as np
217d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanimport pandas as pd
227d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
237d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanfrom bart.common.Utils import area_under_curve
247d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
25e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackmanfrom energy_model import EnergyModel, EnergyModelCapacityError
267d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanfrom perf_analysis import PerfAnalysis
27113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackmanfrom test import LisaTest, experiment_test
287d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanfrom trace import Trace
29e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackmanfrom unittest import SkipTest
307d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
31113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
329b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick BellasiWORKLOAD_PERIOD_MS =  16
33113ce343beab5f54bc93b9d2272dc45affd3705dBrendan JackmanSET_IS_BIG_LITTLE = True
34113ce343beab5f54bc93b9d2272dc45affd3705dBrendan JackmanSET_INITIAL_TASK_UTIL = True
35113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
36113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackmanclass _EnergyModelTest(LisaTest):
377d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
387d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    "Abstract" base class for generic EAS tests using the EnergyModel class
397d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
407d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Subclasses should provide a .workloads member to populate the 'wloads' field
417d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    of the experiments_conf for the Executor. A set of helper methods are
427d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    provided for making assertions about behaviour, most importantly the _test*
437d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    methods which make assertions in a generic way.
447d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
457d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
46113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman    test_conf = {
47113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        "ftrace" : {
48113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            "events" : [
49113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "sched_overutilized",
50113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "sched_energy_diff",
51113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "sched_load_avg_task",
52113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "sched_load_avg_cpu",
53113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "sched_migrate_task",
54d36f1e2a7523c1d1adfae5bd8a6073dd9c2a8919Brendan Jackman                "sched_switch",
55d36f1e2a7523c1d1adfae5bd8a6073dd9c2a8919Brendan Jackman                "cpu_frequency",
56d36f1e2a7523c1d1adfae5bd8a6073dd9c2a8919Brendan Jackman                "cpu_idle",
57d36f1e2a7523c1d1adfae5bd8a6073dd9c2a8919Brendan Jackman                "cpu_capacity",
58113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            ],
59113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        },
60113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        "modules": ["cgroups"],
61113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman    }
62113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
637d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    negative_slack_allowed_pct = 15
647d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """Percentage of RT-App task activations with negative slack allowed"""
657d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
662c9c8c6729331ec1385a616e70e58f1cd2c5d089Brendan Jackman    energy_est_threshold_pct = 5
677d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
687d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Allowed margin for error in estimated energy cost for task placement,
697d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    compared to optimal placment.
707d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
717d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
727d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @classmethod
73113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman    def setUpClass(cls, *args, **kwargs):
74113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        super(_EnergyModelTest, cls).runExperiments(*args, **kwargs)
75113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
76113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman    @classmethod
77e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman    def _getExperimentsConf(cls, test_env):
78e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman        if not test_env.nrg_model:
79e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman            try:
80e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman                test_env.nrg_model = EnergyModel.from_target(test_env.target)
81e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman            except Exception as e:
82e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman                raise SkipTest(
83e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman                    'This test requires an EnergyModel for the platform. '
84e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman                    'Either provide one manually or ensure it can be read '
85e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman                    'from the filesystem: {}'.format(e))
86e4e4b7cc8831a7c7fb96b2cf3dc8bc4d476b4607Brendan Jackman
8714851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman        conf = {
8814851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            'tag' : 'energy_aware',
8914851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            'flags' : ['ftrace', 'freeze_userspace'],
9014851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            'sched_features' : 'ENERGY_AWARE',
9114851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman        }
9214851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman
9314851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman        if 'cpufreq' in test_env.target.modules:
9414851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            available_govs = test_env.target.cpufreq.list_governors(0)
9514851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            if 'schedutil' in available_govs:
9614851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman                conf['cpufreq'] = {'governor' : 'schedutil'}
9714851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            elif 'sched' in available_govs:
9814851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman                conf['cpufreq'] = {'governor' : 'sched'}
9914851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman
1007d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        return {
1017d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'wloads' : cls.workloads,
10214851f7879c9b0b8c9b0b34c8a24407d5d32cd2fBrendan Jackman            'confs' : [conf],
1037d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        }
1047d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
105113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman    @classmethod
106113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman    def _experimentsInit(cls, *args, **kwargs):
107113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        super(_EnergyModelTest, cls)._experimentsInit(*args, **kwargs)
108113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
109113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        if SET_IS_BIG_LITTLE:
110113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            # This flag doesn't exist on mainline-integration kernels, so
111113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            # don't worry if the file isn't present (hence verify=False)
112113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            cls.target.write_value(
113113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "/proc/sys/kernel/sched_is_big_little", 1, verify=False)
114113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
115113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman        if SET_INITIAL_TASK_UTIL:
116113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            # This flag doesn't exist on all kernels, so don't worry if the file
117113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            # isn't present (hence verify=False)
118113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman            cls.target.write_value(
119113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman                "/proc/sys/kernel/sched_initial_task_util", 1024, verify=False)
120113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
121113ce343beab5f54bc93b9d2272dc45affd3705dBrendan Jackman
1227d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def get_task_utils_df(self, experiment):
1237d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1247d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Get a DataFrame with the *expected* utilization of each task over time
1257d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1267d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :param experiment: The :class:Experiment to examine
1277d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :returns: A Pandas DataFrame with a column for each task, showing how
1287d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  the utilization of that task varies over time
1297d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1307d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        util_scale = self.te.nrg_model.capacity_scale
1317d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1327d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        transitions = {}
1337d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        def add_transition(time, task, util):
1347d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            if time not in transitions:
1357d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                transitions[time] = {task: util}
1367d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            else:
1377d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                transitions[time][task] = util
1387d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1397d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # First we'll build a dict D {time: {task_name: util}} where D[t][n] is
1407d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # the expected utilization of task n from time t.
1417d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        for task, params in experiment.wload.params['profile'].iteritems():
1427d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            time = self.get_start_time(experiment) + params['delay']
1437d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            add_transition(time, task, 0)
1447d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            for _ in range(params.get('loops', 1)):
1457d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                for phase in params['phases']:
1467d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    util = (phase.duty_cycle_pct * util_scale / 100.)
1477d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    add_transition(time, task, util)
1487d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    time += phase.duration_s
1497d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            add_transition(time, task, 0)
1507d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1517d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        index = sorted(transitions.keys())
1527d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        df = pd.DataFrame([transitions[k] for k in index], index=index)
1537d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        return df.fillna(method='ffill')
1547d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1557d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def get_task_cpu_df(self, experiment):
1567d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1577d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Get a DataFrame mapping task names to the CPU they ran on
1587d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1597d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Use the sched_switch trace event to find which CPU each task ran
1607d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        on. Does not reflect idleness - tasks not running are shown as running
1617d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        on the last CPU they woke on.
1627d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1637d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :param experiment: The :class:Experiment to examine
1647d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :returns: A Pandas DataFrame with a column for each task, showing the
1657d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  CPU that the task was "on" at each moment in time
1667d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1677d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        tasks = experiment.wload.tasks.keys()
1687d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        trace = self.get_trace(experiment)
1697d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1707d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        df = trace.ftrace.sched_switch.data_frame[['next_comm', '__cpu']]
1717d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        df = df[df['next_comm'].isin(tasks)]
1727d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        df = df.pivot(index=df.index, columns='next_comm').fillna(method='ffill')
1737d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        cpu_df = df['__cpu']
1747d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # Drop consecutive duplicates
1757d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        cpu_df = cpu_df[(cpu_df.shift(+1) != cpu_df).any(axis=1)]
1767d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        return cpu_df
1777d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1787d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def _sort_power_df_columns(self, df):
1797d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1807d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Helper method to re-order the columns of a power DataFrame
1817d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1827d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        This has no significance for code, but when examining DataFrames by hand
1837d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        they are easier to understand if the columns are in a logical order.
1847d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1857d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        node_cpus = [node.cpus for node in self.te.nrg_model.root.iter_nodes()]
1867d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        return pd.DataFrame(df, columns=[c for c in node_cpus if c in df])
1877d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1887d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def get_power_df(self, experiment):
1897d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
1907d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Considering only the task placement, estimate power usage over time
1917d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1927d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Examine a trace and use :meth:EnergyModel.estimate_from_cpu_util to get
1937d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        a DataFrame showing the estimated power usage over time. This assumes
1947d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        perfect cpuidle and cpufreq behaviour.
1957d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
1967d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :param experiment: The :class:Experiment to examine
1977d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :returns: A Pandas DataFrame with a column node in the energy model
1987d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  (keyed with a tuple of the CPUs contained by that node) Shows
1997d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  the estimated power over time.
2007d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2017d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        task_cpu_df = self.get_task_cpu_df(experiment)
2027d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        task_utils_df = self.get_task_utils_df(experiment)
2037d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2047d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        tasks = experiment.wload.tasks.keys()
2057d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2067d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # Create a combined DataFrame with the utilization of a task and the CPU
2077d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # it was running on at each moment. Looks like:
2087d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        #                       utils                  cpus
2097d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        #          task_wmig0 task_wmig1 task_wmig0 task_wmig1
2107d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # 2.375056      102.4      102.4        NaN        NaN
2117d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # 2.375105      102.4      102.4        2.0        NaN
2127d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2137d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        df = pd.concat([task_utils_df, task_cpu_df],
2147d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                       axis=1, keys=['utils', 'cpus'])
2157d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        df = df.sort_index().fillna(method='ffill')
2167d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        nrg_model = self.executor.te.nrg_model
2177d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2187d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        # Now make a DataFrame with the estimated power at each moment.
2197d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        def est_power(row):
2207d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            cpu_utils = [0 for cpu in nrg_model.cpus]
2217d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            for task in tasks:
2227d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                cpu = row['cpus'][task]
2237d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                util = row['utils'][task]
2247d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                if not isnan(cpu):
2257d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    cpu_utils[int(cpu)] += util
2267d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            power = nrg_model.estimate_from_cpu_util(cpu_utils)
2277d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            columns = power.keys()
2287d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            return pd.Series([power[c] for c in columns], index=columns)
2297d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        return self._sort_power_df_columns(df.apply(est_power, axis=1))
2307d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2317d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def get_expected_power_df(self, experiment):
2327d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2337d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Estimate *optimal* power usage over time
2347d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2357d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Examine a trace and use :meth:get_optimal_placements and
2367d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :meth:EnergyModel.estimate_from_cpu_util to get a DataFrame showing the
2377d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        estimated power usage over time under ideal EAS behaviour.
2387d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2397d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :param experiment: The :class:Experiment to examine
2407d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :returns: A Pandas DataFrame with a column each node in the energy model
2417d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  (keyed with a tuple of the CPUs contained by that node) and a
2427d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  "power" column with the sum of other columns. Shows the
2437d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                  estimated *optimal* power over time.
2447d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2457d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        task_utils_df = self.get_task_utils_df(experiment)
2467d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2477d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        nrg_model = self.te.nrg_model
2487d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2497d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        def exp_power(row):
2507d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            task_utils = row.to_dict()
2517d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            expected_utils = nrg_model.get_optimal_placements(task_utils)
2527d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            power = nrg_model.estimate_from_cpu_util(expected_utils[0])
2537d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            columns = power.keys()
2547d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            return pd.Series([power[c] for c in columns], index=columns)
2557d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        return self._sort_power_df_columns(
2567d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            task_utils_df.apply(exp_power, axis=1))
2577d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2587d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def _test_slack(self, experiment, tasks):
2597d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2607d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Assert that the RTApp workload was given enough performance
2617d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2627d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Use :class:PerfAnalysis to find instances where the experiment's RT-App
2637d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        workload wasn't able to complete its activations (i.e. its reported
2647d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        "slack" was negative). Assert that this happened less that
2657d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        ``negative_slack_allowed_pct`` percent of the time.
2667d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2677d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        :meth:_test_task_placement asserts that estimated energy usage was
2687d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        low. That will pass for runs where too *little* energy was used,
2697d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        compromising performance. This method provides a separate test to
2707d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        counteract that problem.
2717d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2727d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2737d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        pa = PerfAnalysis(experiment.out_dir)
2747d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        for task in tasks:
2757d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            slack = pa.df(task)["Slack"]
2767d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2777d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            bad_activations_pct = len(slack[slack < 0]) * 100. / len(slack)
2787d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            if bad_activations_pct > self.negative_slack_allowed_pct:
2797d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                raise AssertionError("task {} missed {}% of activations".format(
2807d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    task, bad_activations_pct))
2817d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2827d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def _test_task_placement(self, experiment, tasks):
2837d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2847d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Test that task placement was energy-efficient
2857d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2867d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        Use :meth:get_expected_power_df and :meth:get_power_df to estimate
2877d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        optimal and observed power usage for task placements of the experiment's
2887d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        workload. Assert that the observed power does not exceed the optimal
2897d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        power by more than 20%.
2907d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        """
2917d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        exp_power = self.get_expected_power_df(experiment)
2927d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        est_power = self.get_power_df(experiment)
2937d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2947d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        exp_energy = area_under_curve(exp_power.sum(axis=1), method='rect')
2957d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        est_energy = area_under_curve(est_power.sum(axis=1), method='rect')
2967d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
2977d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        msg = 'Estimated {} bogo-Joules to run workload, expected {}'.format(
2987d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            est_energy, exp_energy)
2997d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        threshold = exp_energy * (1 + (self.energy_est_threshold_pct / 100.))
3007d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self.assertLess(est_energy, threshold, msg=msg)
3017d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
3027d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass OneSmallTask(_EnergyModelTest):
3037d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3047d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for a single 20% task over 2 seconds
3057d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3067d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
3077d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        'one_small' : {
3087d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'type' : 'rt-app',
3097d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'conf' : {
3107d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'class' : 'periodic',
3117d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'params' : {
3127d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'duty_cycle_pct': 20,
3137d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'duration_s': 2,
3149b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick Bellasi                    'period_ms': WORKLOAD_PERIOD_MS,
3157d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
3167d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'tasks' : 1,
3177d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'prefix' : 'many',
3187d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
3197d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
3207d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
3217d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
3227d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
3237d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
3247d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
3257d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
3267d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
3277d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
3287d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass ThreeSmallTasks(_EnergyModelTest):
3297d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3307d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for 3 20% tasks over 2 seconds
3317d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
332a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman
333a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # The energy estimation for this test is probably not very accurate and this
334a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # isn't a very realistic workload. It doesn't really matter if we pick an
335a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # "ideal" task placement for this workload, we just want to avoid using big
336a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # CPUs in a big.LITTLE system. So use a larger energy threshold that
337a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # hopefully prevents too much use of big CPUs but otherwise is flexible in
338a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # allocation of LITTLEs.
339a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    energy_est_threshold_pct = 20
340a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman
3417d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
3427d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        'three_small' : {
3437d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'type' : 'rt-app',
3447d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'conf' : {
3457d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'class' : 'periodic',
3467d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'params' : {
3477d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'duty_cycle_pct': 20,
3487d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'duration_s': 2,
3499b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick Bellasi                    'period_ms': WORKLOAD_PERIOD_MS,
3507d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
3517d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'tasks' : 3,
3527d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'prefix' : 'many',
3537d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
3547d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
3557d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
3567d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
3577d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
3587d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
3597d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
3607d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
3617d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
3627d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
3637d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass TwoBigTasks(_EnergyModelTest):
3647d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3657d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for 2 80% tasks over 2 seconds
3667d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3677d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
3687d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        'two_big' : {
3697d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'type' : 'rt-app',
3707d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'conf' : {
3717d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'class' : 'periodic',
3727d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'params' : {
3737d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'duty_cycle_pct': 80,
3747d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'duration_s': 2,
3759b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick Bellasi                    'period_ms': WORKLOAD_PERIOD_MS,
3767d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
3777d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'tasks' : 2,
3787d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'prefix' : 'many',
3797d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
3807d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
3817d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
3827d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
3837d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
3847d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
3857d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
3867d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
3877d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
3887d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
3897d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass TwoBigThreeSmall(_EnergyModelTest):
3907d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3917d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for 2 70% tasks and 3 10% tasks over 2 seconds
3927d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
3937d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
3947d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        'two_big_three_small' : {
3957d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'type' : 'rt-app',
3967d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'conf' : {
3977d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'class' : 'profile',
3987d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'params' : {
3997d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'large' : {
4007d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'kind' : 'Periodic',
4017d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'params' : {
4027d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'duty_cycle_pct': 70,
4037d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'duration_s': 2,
4047d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'period_ms': WORKLOAD_PERIOD_MS,
4057d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        },
4067d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'tasks' : 2,
4077d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    },
4087d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'small' : {
4097d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'kind' : 'Periodic',
4107d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'params' : {
4117d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'duty_cycle_pct': 10,
4127d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'duration_s': 2,
4137d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'period_ms': WORKLOAD_PERIOD_MS,
4147d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        },
4157d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'tasks' : 3,
4167d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    },
4177d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
4187d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
4197d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
4207d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
4217d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
4227d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
4237d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
4247d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
4257d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
4267d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
4277d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
4287d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass RampUp(_EnergyModelTest):
4297d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
4307d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for a task ramping from 5% up to 70% over 2 seconds
4317d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
4327d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
4337d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        "ramp_up" : {
4347d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            "type": "rt-app",
4357d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            "conf" : {
4367d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                "class"  : "profile",
4377d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                "params"  : {
4387d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    "r5_10-60" : {
4397d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        "kind"   : "Ramp",
4407d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        "params" : {
4419b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick Bellasi                            'period_ms': WORKLOAD_PERIOD_MS,
4427d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "start_pct" :  5,
4437d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "end_pct"   : 70,
4447d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "delta_pct" :  5,
4457d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "time_s"    :  2,
4467d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                         },
4477d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    },
4487d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
4497d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
4507d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
4517d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
4527d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
4537d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
4547d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
4557d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
4567d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
4577d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
4587d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
4597d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
4607d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass RampDown(_EnergyModelTest):
4617d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
4627d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for a task ramping from 70% down to 5% over 2 seconds
4637d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
464a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman
465a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # The main purpose of this test is to ensure that as it reduces in load, a
466a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # task is migrated from big to LITTLE CPUs on a big.LITTLE system.
467a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # This migration naturally happens some time _after_ it could possibly be
468a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # done, since there must be some hysteresis to avoid a performance cost.
469a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    # Therefore allow a larger energy usage threshold
470a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman    energy_est_threshold_pct = 15
471a31e7b99a94205bcbf47591f3bad573fe1528edbBrendan Jackman
4727d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
4737d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        "ramp_down" : {
4747d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            "type": "rt-app",
4757d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            "conf" : {
4767d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                "class"  : "profile",
4777d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                "params"  : {
4787d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    "r5_10-60" : {
4797d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        "kind"   : "Ramp",
4807d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        "params" : {
4819b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick Bellasi                            'period_ms': WORKLOAD_PERIOD_MS,
4827d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "start_pct" : 70,
4837d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "end_pct"   :  5,
4847d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "delta_pct" :  5,
4857d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            "time_s"    :  2,
4867d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                         },
4877d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    },
4887d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
4897d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
4907d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
4917d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
4927d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
4937d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
4947d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
4957d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
4967d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
4977d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
4987d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
4997d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman
5007d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackmanclass EnergyModelWakeMigration(_EnergyModelTest):
5017d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
5027d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    Test EAS for tasks alternating beetween 10% and 50%
5037d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    """
5047d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    workloads = {
5057d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        'em_wake_migration' : {
5067d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'type' : 'rt-app',
5077d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            'conf' : {
5087d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'class' : 'profile',
5097d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                'params' : {
5107d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    'wmig' : {
5117d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'kind' : 'Step',
5127d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'params' : {
5139b27514de6d848e9fcf81f27fcce652a5dc89af9Patrick Bellasi                            "period_ms" : WORKLOAD_PERIOD_MS,
5147d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'start_pct': 10,
5157d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'end_pct': 50,
5167d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'time_s': 2,
5177d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                            'loops': 2
5187d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        },
5197d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        # Create one task for each big cpu
5207d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                        'tasks' : 'big',
5217d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                    },
5227d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman                },
5237d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman            },
5247d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        },
5257d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    }
5267d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
5277d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_slack(self, experiment, tasks):
5287d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_slack(experiment, tasks)
5297d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    @experiment_test
5307d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman    def test_task_placement(self, experiment, tasks):
5317d7b5c537c77f762a1d0ac672bc16152f5a5643eBrendan Jackman        self._test_task_placement(experiment, tasks)
532