mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-03-21 01:59:13 +00:00
Merge pull request #133 from mcgeagh/uxperf
CpuUtilisationTimeline added. This now will generate cpu utilisation …
This commit is contained in:
commit
df8ef6be6b
20
wlauto/result_processors/cpustate.py
Normal file → Executable file
20
wlauto/result_processors/cpustate.py
Normal file → Executable file
@ -102,7 +102,13 @@ class CpuStatesProcessor(ResultProcessor):
|
||||
Create a CSV with the timeline of core power states over the course of the run
|
||||
as well as the usual stats reports.
|
||||
"""),
|
||||
|
||||
Parameter('create_utilization_timeline', kind=bool, default=False,
|
||||
description="""
|
||||
Create a CSV with the timeline of cpu(s) utilisation over the course of the run
|
||||
as well as the usual stats reports.
|
||||
The values generated are floating point numbers, normalised based on the maximum
|
||||
frequency of the cluster.
|
||||
"""),
|
||||
]
|
||||
|
||||
def validate(self):
|
||||
@ -130,6 +136,7 @@ class CpuStatesProcessor(ResultProcessor):
|
||||
self.idle_state_names = [idle_states[i] for i in sorted(idle_states.keys())]
|
||||
self.num_idle_states = len(self.idle_state_names)
|
||||
self.iteration_reports = OrderedDict()
|
||||
self.max_freq_list = []
|
||||
# priority -19: just higher than the slow_start of instrumentation
|
||||
signal.connect(self.set_initial_state, signal.BEFORE_WORKLOAD_EXECUTION, priority=-19)
|
||||
|
||||
@ -139,12 +146,17 @@ class CpuStatesProcessor(ResultProcessor):
|
||||
# Write initial frequencies into the trace.
|
||||
# NOTE: this assumes per-cluster DVFS, that is valid for devices that
|
||||
# currently exist. This will need to be updated for per-CPU DFS.
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.logger.debug('Writing initial frequencies into trace...')
|
||||
device = context.device
|
||||
cluster_freqs = {}
|
||||
cluster_max_freqs = {}
|
||||
self.max_freq_list = []
|
||||
for c in unique(device.core_clusters):
|
||||
cluster_freqs[c] = device.get_cluster_cur_frequency(c)
|
||||
cluster_max_freqs[c] = device.get_cluster_max_frequency(c)
|
||||
for i, c in enumerate(device.core_clusters):
|
||||
self.max_freq_list.append(cluster_max_freqs[c])
|
||||
entry = 'CPU {} FREQUENCY: {} kHZ'.format(i, cluster_freqs[c])
|
||||
device.set_sysfile_value('/sys/kernel/debug/tracing/trace_marker',
|
||||
entry, verify=False)
|
||||
@ -165,6 +177,10 @@ class CpuStatesProcessor(ResultProcessor):
|
||||
timeline_csv_file = os.path.join(context.output_directory, 'power_states.csv')
|
||||
else:
|
||||
timeline_csv_file = None
|
||||
if self.create_utilization_timeline:
|
||||
cpu_utilisation = os.path.join(context.output_directory, 'cpu_utilisation.csv')
|
||||
else:
|
||||
cpu_utilisation = None
|
||||
parallel_report, powerstate_report = report_power_stats( # pylint: disable=unbalanced-tuple-unpacking
|
||||
trace_file=trace.path,
|
||||
idle_state_names=self.idle_state_names,
|
||||
@ -175,6 +191,8 @@ class CpuStatesProcessor(ResultProcessor):
|
||||
first_system_state=self.first_system_state,
|
||||
use_ratios=self.use_ratios,
|
||||
timeline_csv_file=timeline_csv_file,
|
||||
cpu_utilisation=cpu_utilisation,
|
||||
max_freq_list=self.max_freq_list,
|
||||
)
|
||||
if parallel_report is None:
|
||||
self.logger.warning('No power state reports generated; are power '
|
||||
|
55
wlauto/utils/power.py
Normal file → Executable file
55
wlauto/utils/power.py
Normal file → Executable file
@ -515,6 +515,39 @@ class PowerStateStatsReport(object):
|
||||
for s in stats])
|
||||
|
||||
|
||||
class CpuUtilisationTimeline(object):
|
||||
|
||||
def __init__(self, filepath, core_names, max_freq_list):
|
||||
self.filepath = filepath
|
||||
self._wfh = open(filepath, 'w')
|
||||
self.writer = csv.writer(self._wfh)
|
||||
if core_names:
|
||||
headers = ['ts'] + ['{} CPU{}'.format(c, i)
|
||||
for i, c in enumerate(core_names)]
|
||||
self.writer.writerow(headers)
|
||||
self._max_freq_list = max_freq_list
|
||||
|
||||
def update(self, timestamp, core_states): # NOQA
|
||||
row = [timestamp]
|
||||
for core, [idle_state, frequency] in enumerate(core_states):
|
||||
if idle_state == -1:
|
||||
if frequency == UNKNOWN_FREQUENCY:
|
||||
frequency = 0
|
||||
elif idle_state is None:
|
||||
frequency = 0
|
||||
else:
|
||||
frequency = 0
|
||||
if core < len(self._max_freq_list):
|
||||
frequency /= float(self._max_freq_list[core])
|
||||
row.append(frequency)
|
||||
else:
|
||||
logger.warning('Unable to detect max frequency for this core. Cannot log utilisation value')
|
||||
self.writer.writerow(row)
|
||||
|
||||
def report(self):
|
||||
self._wfh.close()
|
||||
|
||||
|
||||
def build_idle_domains(core_clusters, # NOQA
|
||||
num_states,
|
||||
first_cluster_state=None,
|
||||
@ -577,7 +610,8 @@ def build_idle_domains(core_clusters, # NOQA
|
||||
def report_power_stats(trace_file, idle_state_names, core_names, core_clusters,
|
||||
num_idle_states, first_cluster_state=sys.maxint,
|
||||
first_system_state=sys.maxint, use_ratios=False,
|
||||
timeline_csv_file=None, filter_trace=False):
|
||||
timeline_csv_file=None, filter_trace=False,
|
||||
cpu_utilisation=None, max_freq_list=None):
|
||||
# pylint: disable=too-many-locals
|
||||
trace = TraceCmdTrace(filter_markers=filter_trace)
|
||||
ps_processor = PowerStateProcessor(core_clusters,
|
||||
@ -592,6 +626,11 @@ def report_power_stats(trace_file, idle_state_names, core_names, core_clusters,
|
||||
if timeline_csv_file:
|
||||
reporters.append(PowerStateTimeline(timeline_csv_file,
|
||||
core_names, idle_state_names))
|
||||
if cpu_utilisation:
|
||||
if max_freq_list:
|
||||
reporters.append(CpuUtilisationTimeline(cpu_utilisation, core_names, max_freq_list))
|
||||
else:
|
||||
logger.warning('Maximum frequencies not found. Cannot normalise. Skipping CPU Utilisation Timeline')
|
||||
|
||||
event_stream = trace.parse(trace_file, names=['cpu_idle', 'cpu_frequency', 'print'])
|
||||
transition_stream = stream_cpu_power_transitions(event_stream)
|
||||
@ -624,6 +663,8 @@ def main():
|
||||
use_ratios=args.ratios,
|
||||
timeline_csv_file=args.timeline_file,
|
||||
filter_trace=(not args.no_trace_filter),
|
||||
cpu_utilisation=args.cpu_utilisation,
|
||||
max_freq_list=args.max_freq_list,
|
||||
)
|
||||
parallel_report.write(os.path.join(args.output_directory, 'parallel.csv'))
|
||||
powerstate_report.write(os.path.join(args.output_directory, 'cpustate.csv'))
|
||||
@ -698,6 +739,18 @@ def parse_arguments(): # NOQA
|
||||
A timeline of core power states will be written to the specified file in
|
||||
CSV format.
|
||||
''')
|
||||
parser.add_argument('-u', '--cpu-utilisation', metavar='FILE',
|
||||
help='''
|
||||
A timeline of cpu(s) utilisation will be written to the specified file in
|
||||
CSV format.
|
||||
''')
|
||||
parser.add_argument('-m', '--max-freq-list', action=SplitListAction, default=[],
|
||||
help='''
|
||||
Comma-separated list of core maximum frequencies for the device on which
|
||||
the trace was collected.
|
||||
Only required if --cpu-utilisation is set.
|
||||
This is used to normalise the frequencies to obtain percentage utilisation.
|
||||
''')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user