mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-03-25 20:18:47 +00:00
CpuUtilisationTimeline added. This now will generate cpu utilisation based on frequencies and a number of samples
Fixed error in percentage when frequency is 'None'. Now default to 0 in these cases cpu_utilisation is now a separate parameter in cpustate. Now generates a floating point number representing the utilisation based on the maximum frequency of the capture. No longer performs averaging of values, this can be done as a post-processing step cpu utilisation now based on the max cpu freq per core, not max captured freq overall
This commit is contained in:
parent
d303ab2b50
commit
8a3186e1c8
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
|
Create a CSV with the timeline of core power states over the course of the run
|
||||||
as well as the usual stats reports.
|
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):
|
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.idle_state_names = [idle_states[i] for i in sorted(idle_states.keys())]
|
||||||
self.num_idle_states = len(self.idle_state_names)
|
self.num_idle_states = len(self.idle_state_names)
|
||||||
self.iteration_reports = OrderedDict()
|
self.iteration_reports = OrderedDict()
|
||||||
|
self.max_freq_list = []
|
||||||
# priority -19: just higher than the slow_start of instrumentation
|
# priority -19: just higher than the slow_start of instrumentation
|
||||||
signal.connect(self.set_initial_state, signal.BEFORE_WORKLOAD_EXECUTION, priority=-19)
|
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.
|
# Write initial frequencies into the trace.
|
||||||
# NOTE: this assumes per-cluster DVFS, that is valid for devices that
|
# NOTE: this assumes per-cluster DVFS, that is valid for devices that
|
||||||
# currently exist. This will need to be updated for per-CPU DFS.
|
# 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...')
|
self.logger.debug('Writing initial frequencies into trace...')
|
||||||
device = context.device
|
device = context.device
|
||||||
cluster_freqs = {}
|
cluster_freqs = {}
|
||||||
|
cluster_max_freqs = {}
|
||||||
|
self.max_freq_list = []
|
||||||
for c in unique(device.core_clusters):
|
for c in unique(device.core_clusters):
|
||||||
cluster_freqs[c] = device.get_cluster_cur_frequency(c)
|
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):
|
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])
|
entry = 'CPU {} FREQUENCY: {} kHZ'.format(i, cluster_freqs[c])
|
||||||
device.set_sysfile_value('/sys/kernel/debug/tracing/trace_marker',
|
device.set_sysfile_value('/sys/kernel/debug/tracing/trace_marker',
|
||||||
entry, verify=False)
|
entry, verify=False)
|
||||||
@ -165,6 +177,10 @@ class CpuStatesProcessor(ResultProcessor):
|
|||||||
timeline_csv_file = os.path.join(context.output_directory, 'power_states.csv')
|
timeline_csv_file = os.path.join(context.output_directory, 'power_states.csv')
|
||||||
else:
|
else:
|
||||||
timeline_csv_file = None
|
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
|
parallel_report, powerstate_report = report_power_stats( # pylint: disable=unbalanced-tuple-unpacking
|
||||||
trace_file=trace.path,
|
trace_file=trace.path,
|
||||||
idle_state_names=self.idle_state_names,
|
idle_state_names=self.idle_state_names,
|
||||||
@ -175,6 +191,8 @@ class CpuStatesProcessor(ResultProcessor):
|
|||||||
first_system_state=self.first_system_state,
|
first_system_state=self.first_system_state,
|
||||||
use_ratios=self.use_ratios,
|
use_ratios=self.use_ratios,
|
||||||
timeline_csv_file=timeline_csv_file,
|
timeline_csv_file=timeline_csv_file,
|
||||||
|
cpu_utilisation=cpu_utilisation,
|
||||||
|
max_freq_list=self.max_freq_list,
|
||||||
)
|
)
|
||||||
if parallel_report is None:
|
if parallel_report is None:
|
||||||
self.logger.warning('No power state reports generated; are power '
|
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])
|
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
|
def build_idle_domains(core_clusters, # NOQA
|
||||||
num_states,
|
num_states,
|
||||||
first_cluster_state=None,
|
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,
|
def report_power_stats(trace_file, idle_state_names, core_names, core_clusters,
|
||||||
num_idle_states, first_cluster_state=sys.maxint,
|
num_idle_states, first_cluster_state=sys.maxint,
|
||||||
first_system_state=sys.maxint, use_ratios=False,
|
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
|
# pylint: disable=too-many-locals
|
||||||
trace = TraceCmdTrace(filter_markers=filter_trace)
|
trace = TraceCmdTrace(filter_markers=filter_trace)
|
||||||
ps_processor = PowerStateProcessor(core_clusters,
|
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:
|
if timeline_csv_file:
|
||||||
reporters.append(PowerStateTimeline(timeline_csv_file,
|
reporters.append(PowerStateTimeline(timeline_csv_file,
|
||||||
core_names, idle_state_names))
|
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'])
|
event_stream = trace.parse(trace_file, names=['cpu_idle', 'cpu_frequency', 'print'])
|
||||||
transition_stream = stream_cpu_power_transitions(event_stream)
|
transition_stream = stream_cpu_power_transitions(event_stream)
|
||||||
@ -624,6 +663,8 @@ def main():
|
|||||||
use_ratios=args.ratios,
|
use_ratios=args.ratios,
|
||||||
timeline_csv_file=args.timeline_file,
|
timeline_csv_file=args.timeline_file,
|
||||||
filter_trace=(not args.no_trace_filter),
|
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'))
|
parallel_report.write(os.path.join(args.output_directory, 'parallel.csv'))
|
||||||
powerstate_report.write(os.path.join(args.output_directory, 'cpustate.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
|
A timeline of core power states will be written to the specified file in
|
||||||
CSV format.
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user