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