From 3b41f69762c74099a7bcc615c91b8f019e3bdb1f Mon Sep 17 00:00:00 2001 From: Sebastian Goscik Date: Thu, 5 Nov 2015 14:47:28 +0000 Subject: [PATCH] Added freqsweep instrument Added an instrument to sweep workloads across all available frequenices --- wlauto/instrumentation/freqsweep/__init__.py | 141 +++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100755 wlauto/instrumentation/freqsweep/__init__.py diff --git a/wlauto/instrumentation/freqsweep/__init__.py b/wlauto/instrumentation/freqsweep/__init__.py new file mode 100755 index 00000000..a2c52655 --- /dev/null +++ b/wlauto/instrumentation/freqsweep/__init__.py @@ -0,0 +1,141 @@ +# Copyright 2015 ARM Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# pylint: disable=access-member-before-definition,attribute-defined-outside-init + +import os +from wlauto import Instrument, Parameter +from wlauto.exceptions import ConfigError, InstrumentError +from wlauto.utils.types import caseless_string + + +class FreqSweep(Instrument): + name = 'freq_sweep' + description = """ + Sweeps workloads through all available frequencies on a device. + + When enabled this instrument will take all workloads specified in an agenda + and run them at all available frequencies for all clusters. + + Recommendations: + - Setting the runner to 'by_spec' increases the chance of successfully + completing an agenda without encountering hotplug issues + - If possible disable dynamic hotplug on the target device + """ + + parameters = [ + Parameter('sweeps', kind=list, + description=""" + By default this instrument will sweep across all available + frequencies for all available clusters. If you wish to only + sweep across certain frequencies on particular clusters you + can do so by specifying this parameter. + + Sweeps should be a lists of dictionaries that can contain: + - Cluster (mandatory): The name of the cluster this sweep will be + performed on. E.g A7 + - Frequencies: A list of frequencies (in KHz) to use. If this is + not provided all frequencies available for this + cluster will be used. + E.g: [800000, 900000, 100000] + - label: Workload specs will be named '{spec id}_{label}_{frequency}'. + If a label is not provided it will be named 'sweep{sweep No.}' + + Example sweep specification: + + freq_sweep: + sweeps: + - cluster: A53 + label: littles + frequencies: [800000, 900000, 100000] + - cluster: A57 + label: bigs + """), + ] + + def validate(self): + if not self.device.core_names: + raise ConfigError('The Device does not appear to have core_names configured.') + + def initialize(self, context): # pylint: disable=r0912 + if not self.device.is_rooted: + raise InstrumentError('The device must be rooted to sweep frequencies') + + if 'userspace' not in self.device.list_available_cluster_governors(0): + raise InstrumentError("'userspace' cpufreq governor must be enabled") + + # Create sweeps for each core type using num_cpus cores + if not self.sweeps: + self.sweeps = [] + for core in set(self.device.core_names): + sweep_spec = {} + sweep_spec['cluster'] = core + sweep_spec['label'] = core + self.sweeps.append(sweep_spec) + + new_specs = [] + old_specs = [] + for job in context.runner.job_queue: + if job.spec not in old_specs: + old_specs.append(job.spec) + + # Validate sweeps, add missing sections and create workload specs + for i, sweep_spec in enumerate(self.sweeps): + if 'cluster' not in sweep_spec: + raise ConfigError('cluster must be define for all sweeps') + # Check if cluster exists on device + if caseless_string(sweep_spec['cluster']) not in self.device.core_names: + raise ConfigError('Only {} cores are present on this device, you specified {}' + .format(", ".join(set(self.device.core_names)), sweep_spec['cluster'])) + + # Default to all available frequencies + if 'frequencies' not in sweep_spec: + self.device.enable_cpu(self.device.core_names.index(sweep_spec['cluster'])) + sweep_spec['frequencies'] = self.device.list_available_core_frequencies(sweep_spec['cluster']) + + # Check that given frequencies are valid of the core cluster + else: + self.device.enable_cpu(self.device.core_names.index(sweep_spec['cluster'])) + available_freqs = self.device.list_available_core_frequencies(sweep_spec['cluster']) + for freq in sweep_spec['frequencies']: + if freq not in available_freqs: + raise ConfigError('Frequency {} is not supported by {} cores'.format(freq, sweep_spec['cluster'])) + + # Add default labels + if 'label' not in sweep_spec: + sweep_spec['label'] = "sweep{}".format(i + 1) + + new_specs.extend(self.get_sweep_workload_specs(old_specs, sweep_spec, context)) + + # Update config to refect jobs that will actually run. + context.config.workload_specs = new_specs + config_file = os.path.join(context.host_working_directory, 'run_config.json') + with open(config_file, 'wb') as wfh: + context.config.serialize(wfh) + context.runner.init_queue(new_specs) + + def get_sweep_workload_specs(self, old_specs, sweep_spec, context): + new_specs = [] + for old_spec in old_specs: + for freq in sweep_spec['frequencies']: + spec = old_spec.copy() + spec.runtime_parameters['{}_governor'.format(sweep_spec['cluster'])] = "userspace" + spec.runtime_parameters['{}_frequency'.format(sweep_spec['cluster'])] = freq + spec.id = '{}_{}_{}'.format(spec.id, sweep_spec['label'], freq) + spec.classifiers['core'] = sweep_spec['cluster'] + spec.classifiers['freq'] = freq + spec.load(self.device, context.config.ext_loader) + spec.workload.init_resources(context) + spec.workload.validate() + new_specs.append(spec) + return new_specs