def get_rtapp_profile(cls, plat_info): profile = {} cpus = cls.get_migration_cpus(plat_info) for task in ["migr", "static0", "static1"]: # An empty RTATask just to sum phases up profile[task] = RTATask() common_phase_settings = dict( duration_s=cls.PHASE_DURATION_S, period_ms=cls.TASK_PERIOD_MS, ) for cpu in cpus: # A task that will migrate to another CPU profile["migr"] += Periodic( duty_cycle_pct=cls.unscaled_utilization(plat_info, cpu, 20), cpus=[cpu], **common_phase_settings) # Just some tasks that won't move to get some background utilization profile["static0"] += Periodic( duty_cycle_pct=cls.unscaled_utilization( plat_info, cpus[0], 30), cpus=[cpus[0]], **common_phase_settings) profile["static1"] += Periodic( duty_cycle_pct=cls.unscaled_utilization( plat_info, cpus[1], 20), cpus=[cpus[1]], **common_phase_settings) return profile
def get_rtapp_profile(cls, plat_info): cpus = list(range(plat_info['cpus-count'])) # We're pinning stuff in the first phase, so give it ample time to # clean the pinned logic out of balance_interval free_time_s = 1.1 * cls._get_max_lb_interval(plat_info) # Ideally we'd like the different tasks not to complete at the same time # (hence the "staggered" name), but this depends on a lot of factors # (capacity ratios, available frequencies, thermal conditions...) so the # best we can do is wing it. stagger_s = cls._get_lb_interval(plat_info) * 1.5 profile = {} for cpu in cpus: profile[f"{cls.task_prefix}{cpu}"] = ( Periodic(duty_cycle_pct=0, duration_s=cls.IDLING_DELAY_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu]) + Periodic(duty_cycle_pct=100, duration_s=cls.PIN_DELAY_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu]) + Periodic( duty_cycle_pct=100, # Introduce staggered task completions duration_s=free_time_s + cpu * stagger_s, period_ms=cls.TASK_PERIOD_MS, cpus=cpus)) return profile
def get_rtapp_profile(cls, plat_info): cpus = list(range(plat_info['cpus-count'])) # We're pinning stuff in the first phase, so give it ample time to # clean the pinned logic out of balance_interval free_time_s = 1.1 * cls._get_max_lb_interval(plat_info) stagger_s = free_time_s // (10 * len(cpus)) profile = {} for cpu in cpus: profile["{}{}".format(cls.task_prefix, cpu)] = ( Periodic(duty_cycle_pct=0, duration_s=cls.IDLING_DELAY_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu]) + Periodic(duty_cycle_pct=100, duration_s=cls.PIN_DELAY_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu]) + Periodic( duty_cycle_pct=100, # Introduce staggered task completions duration_s=free_time_s + cpu * stagger_s, period_ms=cls.TASK_PERIOD_MS, cpus=cpus)) return profile
def test_invalid_composition(self): """Test that you can't compose tasks with a delay in the second task""" t1 = Periodic() t2 = Periodic(delay_s=1) # Should work fine if delayed task is the first one try: t3 = t2 + t1 except Exception as e: raise AssertionError("Couldn't compose tasks: {}".format(e)) # But not the other way around with self.assertRaises(ValueError): t3 = t1 + t2
def get_rtapp_profile(cls, plat_info): """ This class method is in charge of generating an rt-app profile, to configure the workload that will be run using :meth:`lisa.tests.base.RTATestBundle.run_rtapp`. It can access any information in the given :class:`lisa.platforms.PlatformInfo` in order to obtain a workload tailored to the capacity of the CPUs of the target, the available frequencies and so on. """ # Build a list of the CPU IDs that are available cpus = list(range(plat_info['cpus-count'])) # The profile is a dictionary of task names (keys) to # lisa.wlgen.RTATask instances # https://lisa-linux-integrated-system-analysis.readthedocs.io/en/master/workloads.html profile = {} for cpu in cpus: # Compute a utilization needed to fill 50% of ``cpu`` capacity. util = cls.unscaled_utilization(plat_info, cpu, 50) # A Periodic task has a period, and a duty_cycle (which really is a # target utilization). LISA will run rt-app calibration if needed # (it can be provided by the user in the platform information) profile["{}_{}".format(cls.task_prefix, cpu)] = Periodic( duty_cycle_pct=util, duration_s=1, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu]) return profile
def get_rtapp_profile(cls, plat_info): rtapp_profile = {} rtapp_profile[cls.task_prefix] = Periodic(duty_cycle_pct=50, duration_s=30, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def _get_calib_conf(self, calibration): profile = {"test": Periodic()} rtapp = RTA.by_profile( self.target, name='test', res_dir=self.res_dir, profile=profile, calibration=calibration) with open(rtapp.local_json) as fh: return json.load(fh)['global']['calibration']
def get_rtapp_profile(cls, plat_info): rtapp_profile = {} rtapp_profile['rta_stune'] = Periodic( duty_cycle_pct=1, duration_s=3, period_ms=16, ) return rtapp_profile
def get_rtapp_profile(cls, plat_info): duty = cls.get_little_duty_cycle(plat_info, 50) rtapp_profile = {} rtapp_profile[cls.task_name] = Periodic(duty_cycle_pct=duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): # Four CPU's is enough to demonstrate task migration problem cpu_count = min(4, plat_info["cpus-count"]) rtapp_profile = {} for cpu in range(cpu_count): rtapp_profile[f"{cls.task_prefix}{cpu}"] = Periodic( duty_cycle_pct=50, duration_s=30, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): small_duty = cls.get_little_duty_cycle(plat_info, 50) big_duty = cls.get_big_duty_cycle(plat_info) rtapp_profile = {} for i in range(3): rtapp_profile[f"{cls.small_prefix}_{i}"] = Periodic( duty_cycle_pct=small_duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) for i in range(2): rtapp_profile[f"{cls.big_prefix}_{i}"] = Periodic( duty_cycle_pct=big_duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): littles = plat_info["capacity-classes"][0] duty = cls.unscaled_utilization(plat_info, littles[0], 50) rtapp_profile = {} rtapp_profile[cls.task_name] = Periodic(duty_cycle_pct=duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): duty = cls.get_big_duty_cycle(plat_info) rtapp_profile = {} for i in range(2): rtapp_profile[f"{cls.task_prefix}_{i}"] = Periodic( duty_cycle_pct=duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): bigs = plat_info["capacity-classes"][-1] duty = cls.unscaled_utilization(plat_info, bigs[0], 80) rtapp_profile = {} for i in range(2): rtapp_profile["{}_{}".format(cls.task_prefix, i)] = Periodic( duty_cycle_pct=duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): periods = [ Periodic( duty_cycle_pct=(util / PELT_SCALE) * 100, # util to pct duration_s=5, period_ms=cls.TASK_PERIOD_MS, uclamp_min=uclamp_val, uclamp_max=uclamp_val) for uclamp_val, util in cls._get_phases(plat_info) ] return {'task': functools.reduce(lambda a, b: a + b, periods)}
def get_rtapp_profile(cls, plat_info): duty = cls.get_little_duty_cycle(plat_info, 50) rtapp_profile = {} for i in range(3): rtapp_profile["{}_{}".format(cls.task_prefix, i)] = Periodic( duty_cycle_pct=duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS ) return rtapp_profile
def get_rtapp_profile(cls, plat_info): # Run the 50% workload on a CPU with highest capacity cpu = cls.get_max_capa_cpu(plat_info) rtapp_profile = {} rtapp_profile["{}_cpu{}".format(cls.task_prefix, cpu)] = Periodic( duty_cycle_pct=50, duration_s=2, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu]) return rtapp_profile
def get_rtapp_profile(cls, plat_info): littles = plat_info["capacity-classes"][0] bigs = plat_info["capacity-classes"][-1] small_duty = cls.unscaled_utilization(plat_info, littles[0], 50) big_duty = cls.unscaled_utilization(plat_info, bigs[0], 70) rtapp_profile = {} for i in range(3): rtapp_profile["{}_{}".format(cls.small_prefix, i)] = Periodic( duty_cycle_pct=small_duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) for i in range(2): rtapp_profile["{}_{}".format(cls.big_prefix, i)] = Periodic( duty_cycle_pct=big_duty, duration_s=1, period_ms=cls.TASK_PERIOD_MS) return rtapp_profile
def get_rtapp_profile(cls, plat_info): profile = {} cpus = cls.get_migration_cpus(plat_info) for task in ["migrating0", "migrating1"]: # An empty RTATask just to sum phases up profile[task] = RTATask() for i in range(2): # A task that will migrate from CPU A to CPU B profile["migrating0"] += Periodic(duty_cycle_pct=20, duration_s=cls.PHASE_DURATION_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpus[i]]) # A task that will migrate from CPU B to CPU A profile["migrating1"] += Periodic(duty_cycle_pct=20, duration_s=cls.PHASE_DURATION_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpus[1 - i]]) return profile
def get_rtapp_profile(cls, plat_info): cpu = plat_info['capacity-classes'][-1][0] rtapp_profile = {} rtapp_profile['rta_stune'] = Periodic( duty_cycle_pct=1, # very small task, no impact on freq w/o boost duration_s=10, period_ms=16, cpus=[cpu], # pin to big CPU, to focus on frequency selection sched_policy='FIFO' # RT tasks have the boost holding feature so # the frequency should be more stable, and we # shouldn't go to max freq in Android ) return rtapp_profile
def get_rtapp_profile(cls, plat_info, cpu): """ Get a specification for a rt-app workload with the specificied duty cycle, pinned to the given CPU. """ rtapp_profile = {} rtapp_profile["{}_cpu{}".format(cls.task_prefix, cpu)] = Periodic( duty_cycle_pct=10, duration_s=2, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu], ) return rtapp_profile
def get_rtapp_profile(cls, plat_info): cpus = cls.get_migration_cpus(plat_info) make_name = lambda i: 'migrating{}'.format(i) nr_tasks = len(cpus) profile = {make_name(i): RTATask() for i in range(nr_tasks)} # Define one task per CPU, and create all the possible migrations by # shuffling around these tasks for cpus_combi in itertools.permutations(cpus, r=nr_tasks): for i, cpu in enumerate(cpus_combi): profile[make_name(i)] += Periodic( duty_cycle_pct=cls.unscaled_utilization( plat_info, cpu, 50), duration_s=cls.PHASE_DURATION_S, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu], ) return profile
def get_rtapp_profile(cls, plat_info, cpu, freq): """ Get a specification for a rt-app workload with the specificied duty cycle, pinned to the given CPU. """ freq_capa = cls._get_freq_capa(cpu, freq, plat_info) duty_cycle_pct = freq_capa / UTIL_SCALE * 100 # Use half of the capacity at that OPP, so we are sure that the # task will fit even at the lowest OPP duty_cycle_pct //= 2 rtapp_profile = {} rtapp_profile[f"{cls.task_prefix}{cpu}"] = Periodic( duty_cycle_pct=duty_cycle_pct, duration_s=2, period_ms=cls.TASK_PERIOD_MS, cpus=[cpu], ) return rtapp_profile
def test_profile_periodic_smoke(self): """ Smoketest Periodic rt-app workload Creates a workload using Periodic, tests that the JSON has the expected content, then tests that it can be run. """ profile = { "test": Periodic(period_ms=100, duty_cycle_pct=20, duration_s=1) } exp_phases = [{ 'loop': 10, 'run': 20000, 'timer': { 'period': 100000, 'ref': 'test' } }] self._do_test(profile, exp_phases)
def get_rtapp_profile(cls, plat_info): big_cpu = plat_info["capacity-classes"][-1][0] task = ( # Big task Periodic( duty_cycle_pct=75, duration_s=5, period_ms=200, cpus=[big_cpu]) + # Ramp Down Ramp(start_pct=50, end_pct=5, delta_pct=20, time_s=1, period_ms=200, cpus=[big_cpu]) + # Ramp Up Ramp(start_pct=10, end_pct=60, delta_pct=20, time_s=1, period_ms=200, cpus=[big_cpu])) return {'test': task}
from lisa.trace import FtraceCollector, Trace from lisa.utils import setup_logging from lisa.target import Target, TargetConf from lisa.wlgen.rta import RTA, Periodic from lisa.datautils import df_filter_task_ids import pandas as pd setup_logging() target = Target.from_one_conf('conf/lisa/qemu_target_default.yml') #target = Target.from_default_conf() rtapp_profile = {} tasks = [] for cpu in range(4): tasks.append("tsk{}-{}".format(cpu, cpu)) rtapp_profile["tsk{}".format(cpu)] = Periodic(duty_cycle_pct=50, duration_s=120) wload = RTA.by_profile(target, "experiment_wload", rtapp_profile) ftrace_coll = FtraceCollector(target, events=["sched_switch"]) trace_path = os.path.join(wload.res_dir, "trace.dat") with ftrace_coll: wload.run() ftrace_coll.get_trace(trace_path) trace = Trace(trace_path, target.plat_info, events=["sched_switch"]) # sched_switch __comm __pid __cpu __line prev_comm prev_pid prev_prio prev_state next_comm next_pid next_prio df = trace.df_events('sched_switch')[['next_pid', 'next_comm', '__cpu']]
def test_composition(self): """ Test RTA task composition with __add__ Creates a composed workload by +-ing RTATask objects, tests that the JSON has the expected content, then tests running the workload """ light = Periodic(duty_cycle_pct=10, duration_s=1.0, period_ms=10) start_pct = 10 end_pct = 90 delta_pct = 20 ramp = Ramp(start_pct=start_pct, end_pct=end_pct, delta_pct=delta_pct, time_s=1, period_ms=50) heavy = Periodic(duty_cycle_pct=90, duration_s=0.1, period_ms=100) profile = {"test_task": light + ramp + heavy} exp_phases = [ # Light phase: { "loop": 100, "run": 1000, "timer": { "period": 10000, "ref": "test_task" } }, # Ramp phases: { "loop": 20, "run": 5000, "timer": { "period": 50000, "ref": "test_task" } }, { "loop": 20, "run": 15000, "timer": { "period": 50000, "ref": "test_task" } }, { "loop": 20, "run": 25000, "timer": { "period": 50000, "ref": "test_task" } }, { "loop": 20, "run": 35000, "timer": { "period": 50000, "ref": "test_task" } }, { "loop": 20, "run": 45000, "timer": { "period": 50000, "ref": "test_task" } }, # Heavy phase: { "loop": 1, "run": 90000, "timer": { "period": 100000, "ref": "test_task" } } ] self._do_test(profile, exp_phases)