Home | History | Annotate | Download | only in lisa
      1 # SPDX-License-Identifier: Apache-2.0
      2 #
      3 # Copyright (C) 2016, ARM Limited and contributors.
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
      6 # not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 # http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 #
     17 
     18 from collections import OrderedDict
     19 import unittest
     20 from unittest import TestCase
     21 
     22 from energy_model import (EnergyModel, ActiveState, EnergyModelCapacityError,
     23                           EnergyModelNode, EnergyModelRoot, PowerDomain)
     24 
     25 # Import these just to test that they can be constructed
     26 from libs.utils.platforms.juno_energy import juno_energy
     27 from libs.utils.platforms.pixel_energy import pixel_energy
     28 from libs.utils.platforms.hikey_energy import hikey_energy
     29 
     30 """ A very basic test suite for the EnergyModel class."""
     31 
     32 # WARNING!
     33 # Note that the tests below have hard-coded expectations about the result. If
     34 # you change the numbers in this EM, you'll need to recalculate those hard-coded
     35 # values (or just refactor these tests)
     36 
     37 little_cluster_active_states = OrderedDict([
     38     (1000, ActiveState(power=10)),
     39     (2000, ActiveState(power=20)),
     40 ])
     41 
     42 little_cluster_idle_states = OrderedDict([
     43     ('WFI',              5),
     44     ('cpu-sleep-0',      5),
     45     ('cluster-sleep-0',  1),
     46 ])
     47 
     48 little_cpu_active_states = OrderedDict([
     49     (1000, ActiveState(capacity=100, power=100)),
     50     (1500, ActiveState(capacity=150, power=150)),
     51     (2000, ActiveState(capacity=200, power=200)),
     52 ])
     53 
     54 little_cpu_idle_states = OrderedDict([
     55     ('WFI',              5),
     56     ('cpu-sleep-0',      0),
     57     ('cluster-sleep-0',  0),
     58 ])
     59 
     60 littles=[0, 1]
     61 def little_cpu_node(cpu):
     62     return EnergyModelNode(cpu=cpu,
     63                            active_states=little_cpu_active_states,
     64                            idle_states=little_cpu_idle_states)
     65 
     66 big_cluster_active_states = OrderedDict([
     67     (3000, ActiveState(power=30)),
     68     (4000, ActiveState(power=40)),
     69 ])
     70 
     71 big_cluster_idle_states = OrderedDict([
     72     ('WFI',              8),
     73     ('cpu-sleep-0',      8),
     74     ('cluster-sleep-0',  2),
     75 ])
     76 
     77 big_cpu_active_states = OrderedDict([
     78     (3000, ActiveState(capacity=300, power=300)),
     79     (4000, ActiveState(capacity=400, power=400)),
     80 ])
     81 
     82 big_cpu_idle_states = OrderedDict([
     83     ('WFI',              9),
     84     ('cpu-sleep-0',      0),
     85     ('cluster-sleep-0',  0),
     86 ])
     87 
     88 bigs=[2, 3]
     89 
     90 def big_cpu_node(cpu):
     91     return EnergyModelNode(cpu=cpu,
     92                            active_states=big_cpu_active_states,
     93                            idle_states=big_cpu_idle_states)
     94 
     95 em = EnergyModel(
     96     root_node=EnergyModelRoot(children=[
     97         EnergyModelNode(name='cluster_little',
     98                         active_states=little_cluster_active_states,
     99                         idle_states=little_cluster_idle_states,
    100                         children=[little_cpu_node(0),
    101                                   little_cpu_node(1)]),
    102         EnergyModelNode(name='cluster_big',
    103                         active_states=big_cluster_active_states,
    104                         idle_states=big_cluster_idle_states,
    105                         children=[big_cpu_node(2),
    106                                   big_cpu_node(3)])
    107     ]),
    108     root_power_domain=PowerDomain(idle_states=[], children=[
    109         PowerDomain(
    110             idle_states=['cluster-sleep-0'],
    111             children=[PowerDomain(idle_states=['WFI', 'cpu-sleep-0'], cpu=c)
    112                       for c in littles]),
    113         PowerDomain(
    114             idle_states=['cluster-sleep-0'],
    115             children=[PowerDomain(idle_states=['WFI', 'cpu-sleep-0'], cpu=c)
    116                       for c in bigs]),
    117         ]),
    118     freq_domains=[littles, bigs]
    119 )
    120 
    121 class TestInvalid(TestCase):
    122     """Test the sanity checks in EnerygModel setup"""
    123     def test_overlapping_freq_doms(self):
    124         """Can't build an EM with energy nodes overlapping freq domains"""
    125 
    126         # To make this easy we'll just use a single active state everywhere, and
    127         # no idle states
    128         active_states={10000: ActiveState(capacity=1024, power=100)}
    129 
    130         def cpu_node(cpu):
    131             return EnergyModelNode(cpu=cpu,
    132                                    active_states=active_states,
    133                                    idle_states=[])
    134 
    135         root_node = EnergyModelRoot(children=[
    136             EnergyModelNode(
    137                 name='cluster1',
    138                 active_states=active_states,
    139                 idle_states=[],
    140                 children=[cpu_node(0), cpu_node(1)])])
    141 
    142         def cpu_pd(cpu):
    143             return PowerDomain(idle_states=[], cpu=cpu)
    144 
    145         with self.assertRaises(ValueError):
    146             EnergyModel(root_node=root_node,
    147                         root_power_domain=PowerDomain(
    148                             idle_states=[], children=[cpu_pd(0), cpu_pd(1)]),
    149                         freq_domains=[[0], [1]]),
    150 
    151 
    152 class TestOptimalPlacement(TestCase):
    153     def assertPlacementListEqual(self, l1, l2):
    154         """
    155         Assert that a pair of lists of lists contain the same lists in any order
    156         """
    157         s1 = set([tuple(l) for l in l1])
    158         s2 = set([tuple(l) for l in l2])
    159         self.assertSetEqual(s1, s2)
    160 
    161     def test_single_small(self):
    162         placements = em.get_optimal_placements({'task0': 1})
    163         self.assertPlacementListEqual(placements, [[1, 0, 0, 0],
    164                                                    [0, 1, 0, 0]])
    165 
    166     def test_single_big(self):
    167         placements = em.get_optimal_placements({'task0': 350})
    168         self.assertPlacementListEqual(placements, [[0, 0, 350, 0],
    169                                                    [0, 0, 0, 350]])
    170 
    171     def test_packing(self):
    172         tasks = {'task' + str(i) : 10 for i in range(5)}
    173         placements = em.get_optimal_placements(tasks)
    174         total_util = sum(tasks.values())
    175         self.assertPlacementListEqual(placements, [[total_util, 0, 0, 0],
    176                                                    [0, total_util, 0, 0]])
    177 
    178     def test_overutilized_single(self):
    179         self.assertRaises(EnergyModelCapacityError,
    180                           em.get_optimal_placements, {'task0' : 401})
    181 
    182     def test_overutilized_many(self):
    183         total_cap = 400 * 2 + 200 * 2
    184         task_size = 200
    185         tasks = {'task' + str(i): task_size
    186                  for i in range((total_cap / task_size) + 1)}
    187         self.assertRaises(EnergyModelCapacityError,
    188                           em.get_optimal_placements, tasks)
    189 
    190 class TestBiggestCpus(TestCase):
    191     def test_biggest_cpus(self):
    192         self.assertEqual(em.biggest_cpus, [2, 3])
    193 
    194 class TestLittlestCpus(TestCase):
    195     def test_littlest_cpus(self):
    196         self.assertEqual(em.littlest_cpus, [0, 1])
    197 
    198 class TestMaxCap(TestCase):
    199     def test_max_cap(self):
    200         max_caps = [n.max_capacity for n in em.cpu_nodes]
    201         self.assertEqual(max_caps, [200, 200, 400, 400])
    202 
    203 class TestEnergyEst(TestCase):
    204     def test_all_overutilized(self):
    205         big_cpu = 400 * 2
    206         little_cpu =  200 * 2
    207         big_cluster = 40
    208         little_cluster = 20
    209 
    210         total = big_cpu + little_cpu + big_cluster + little_cluster
    211 
    212         power = em.estimate_from_cpu_util([10000] * 4)
    213         exp = {
    214             (0): { 'active': little_cpu, 'idle': 0},
    215             (1): { 'active': little_cpu, 'idle': 0},
    216             (2): { 'active': big_cpu, 'idle': 0},
    217             (3): { 'active': big_cpu, 'idle': 0},
    218             (0, 1): { 'active': little_cluster, 'idle': 0},
    219             (2, 3): { 'active': big_cluster, 'idle': 0}
    220         }
    221         for k, v in power.iteritems():
    222             self.assertAlmostEqual(v, power[k])
    223 
    224     def test_all_idle(self):
    225         self.assertEqual(sum(em.estimate_from_cpu_util([0, 0, 0, 0]).values()),
    226                          0 * 4 # CPU power = 0
    227                          + 2   # big cluster power
    228                          + 1)  # LITTLE cluster power
    229 
    230     def test_one_little_half_lowest(self):
    231         cpu0_util = 100 * 0.5
    232         self.assertEqual(
    233             sum(em.estimate_from_cpu_util([cpu0_util, 0, 0, 0]).values()),
    234                 (0.5 * 100)  # CPU0 active power
    235                 + (0.5 * 5)  # CPU0 idle power
    236                 + (0.5 * 5)  # LITTLE cluster idle power
    237                 + (0.5 * 10) # LITTLE cluster active power
    238                 + 2)         # big cluster power
    239 
    240 class TestIdleStates(TestCase):
    241     def test_zero_util_deepest(self):
    242         self.assertEqual(em.guess_idle_states([0] * 4), ['cluster-sleep-0'] * 4)
    243 
    244     def test_single_cpu_used(self):
    245         states = em.guess_idle_states([0, 0, 0, 1])
    246         self.assertEqual(states, ['cluster-sleep-0', 'cluster-sleep-0',
    247                                   'cpu-sleep-0', 'WFI'])
    248 
    249         states = em.guess_idle_states([0, 1, 0, 0])
    250         self.assertEqual(states, ['cpu-sleep-0', 'WFI',
    251                                   'cluster-sleep-0', 'cluster-sleep-0',])
    252 
    253     def test_all_cpus_used(self):
    254         states = em.guess_idle_states([1, 1, 1, 1])
    255         self.assertEqual(states, ['WFI'] * 4)
    256 
    257     def test_one_cpu_per_cluster(self):
    258         states = em.guess_idle_states([0, 1, 0, 1])
    259         self.assertEqual(states, ['cpu-sleep-0', 'WFI'] * 2)
    260 
    261 class TestFreqs(TestCase):
    262 
    263     def test_zero_util_slowest(self):
    264         self.assertEqual(em.guess_freqs([0] * 4),
    265                          [1000, 1000, 3000, 3000])
    266 
    267     def test_high_util_fastest(self):
    268         self.assertEqual(em.guess_freqs([100000] * 4),
    269                          [2000, 2000, 4000, 4000])
    270 
    271     def test_freq_domains(self):
    272         self.assertEqual(em.guess_freqs([0, 0, 0, 10000]),
    273                          [1000, 1000, 4000, 4000])
    274 
    275         self.assertEqual(em.guess_freqs([0, 10000, 0, 10000]),
    276                          [2000, 2000, 4000, 4000])
    277 
    278         self.assertEqual(em.guess_freqs([0, 10000, 0, 0]),
    279                          [2000, 2000, 3000, 3000])
    280 
    281     def test_middle_freq(self):
    282         self.assertEqual(em.guess_freqs([0, 110, 0, 0]),
    283                          [1500, 1500, 3000, 3000])
    284 
    285 class TestNames(TestCase):
    286     """Test that the default names for CPU nodes get set"""
    287     def test_names(self):
    288         self.assertListEqual([n.name for n in em.cpu_nodes],
    289                              ['cpu0', 'cpu1', 'cpu2', 'cpu3'])
    290 
    291 class TestCpuGroups(TestCase):
    292     """Test the cpu_groups property"""
    293     def test_cpu_groups(self):
    294         self.assertListEqual(em.cpu_groups, [[0, 1], [2, 3]])
    295 
    296 class TestGetCpuCapacity(TestCase):
    297     """Test the get_cpu_capacity method"""
    298     def test_get_cpu_capacity(self):
    299         for node in em.root.iter_leaves():
    300             [cpu] = node.cpus
    301             self.assertEqual(em.get_cpu_capacity(cpu), node.max_capacity)
    302             for freq, active_state in node.active_states.iteritems():
    303                 self.assertEqual(em.get_cpu_capacity(cpu, freq),
    304                                  active_state.capacity)
    305