# SPDX-License-Identifier: Apache-2.0 # # Copyright (C) 2015, ARM Limited and contributors. # # 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. # """ EAS-specific Analysis Module """ import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import pylab as pl from analysis_module import AnalysisModule class EasAnalysis(AnalysisModule): """ Support for EAS signals anaysis :param trace: input Trace object :type trace: :mod:`libs.utils.Trace` """ def __init__(self, trace): super(EasAnalysis, self).__init__(trace) ############################################################################### # DataFrame Getter Methods ############################################################################### ############################################################################### # Plotting Methods ############################################################################### def plotEDiffTime(self, tasks=None, min_usage_delta=None, max_usage_delta=None, min_cap_delta=None, max_cap_delta=None, min_nrg_delta=None, max_nrg_delta=None, min_nrg_diff=None, max_nrg_diff=None): """ Plot energy_diff()-related signals on time axes. """ if not self._trace.hasEvents('sched_energy_diff'): self._log.warning('Event [sched_energy_diff] not found, plot DISABLED!') return df = self._dfg_trace_event('sched_energy_diff') # Filter on 'tasks' if tasks is not None: self._log.info('Plotting EDiff data just for task(s) [%s]', tasks) df = df[df['comm'].isin(tasks)] # Filter on 'usage_delta' if min_usage_delta is not None: self._log.info('Plotting EDiff data just with minimum ' 'usage_delta of [%d]', min_usage_delta) df = df[abs(df['usage_delta']) >= min_usage_delta] if max_usage_delta is not None: self._log.info('Plotting EDiff data just with maximum ' 'usage_delta of [%d]', max_usage_delta) df = df[abs(df['usage_delta']) <= max_usage_delta] # Filter on 'cap_delta' if min_cap_delta is not None: self._log.info('Plotting EDiff data just with minimum ' 'cap_delta of [%d]', min_cap_delta) df = df[abs(df['cap_delta']) >= min_cap_delta] if max_cap_delta is not None: self._log.info('Plotting EDiff data just with maximum ' 'cap_delta of [%d]', max_cap_delta) df = df[abs(df['cap_delta']) <= max_cap_delta] # Filter on 'nrg_delta' if min_nrg_delta is not None: self._log.info('Plotting EDiff data just with minimum ' 'nrg_delta of [%d]', min_nrg_delta) df = df[abs(df['nrg_delta']) >= min_nrg_delta] if max_nrg_delta is not None: self._log.info('Plotting EDiff data just with maximum ' 'nrg_delta of [%d]', max_nrg_delta) df = df[abs(df['nrg_delta']) <= max_nrg_delta] # Filter on 'nrg_diff' if min_nrg_diff is not None: self._log.info('Plotting EDiff data just with minimum ' 'nrg_diff of [%d]', min_nrg_diff) df = df[abs(df['nrg_diff']) >= min_nrg_diff] if max_nrg_diff is not None: self._log.info('Plotting EDiff data just with maximum ' 'nrg_diff of [%d]', max_nrg_diff) df = df[abs(df['nrg_diff']) <= max_nrg_diff] # Grid: setup stats for gris gs = gridspec.GridSpec(4, 3, height_ratios=[2, 4, 2, 4]) gs.update(wspace=0.1, hspace=0.1) # Configure plot fig = plt.figure(figsize=(16, 8*2+4*2+2)) plt.suptitle("EnergyDiff Data", y=.92, fontsize=16, horizontalalignment='center') # Plot1: src and dst CPUs axes = plt.subplot(gs[0, :]) axes.set_title('Source and Destination CPUs') df[['src_cpu', 'dst_cpu']].plot(ax=axes, style=['bo', 'r+']) axes.set_ylim(-1, self._platform['cpus_count']+1) axes.set_xlim(self._trace.x_min, self._trace.x_max) axes.grid(True) axes.set_xticklabels([]) axes.set_xlabel('') self._trace.analysis.status.plotOverutilized(axes) # Plot2: energy and capacity variations axes = plt.subplot(gs[1, :]) axes.set_title('Energy vs Capacity Variations') colors_labels = zip('gbyr', ['Optimal Accept', 'SchedTune Accept', 'SchedTune Reject', 'Suboptimal Reject']) for color, label in colors_labels: subset = df[df.nrg_payoff_group == label] if len(subset) == 0: continue subset[['nrg_diff_pct']].plot(ax=axes, style=[color+'o']) axes.set_xlim(self._trace.x_min, self._trace.x_max) axes.set_yscale('symlog') axes.grid(True) axes.set_xticklabels([]) axes.set_xlabel('') self._trace.analysis.status.plotOverutilized(axes) # Plot3: energy payoff axes = plt.subplot(gs[2, :]) axes.set_title('Energy Payoff Values') for color, label in colors_labels: subset = df[df.nrg_payoff_group == label] if len(subset) == 0: continue subset[['nrg_payoff']].plot(ax=axes, style=[color+'o']) axes.set_xlim(self._trace.x_min, self._trace.x_max) axes.set_yscale('symlog') axes.grid(True) axes.set_xticklabels([]) axes.set_xlabel('') self._trace.analysis.status.plotOverutilized(axes) # Plot4: energy deltas (kernel and host computed values) axes = plt.subplot(gs[3, :]) axes.set_title('Energy Deltas Values') df[['nrg_delta', 'nrg_diff_pct']].plot(ax=axes, style=['ro', 'b+']) axes.set_xlim(self._trace.x_min, self._trace.x_max) axes.grid(True) self._trace.analysis.status.plotOverutilized(axes) # Save generated plots into datadir figname = '{}/{}ediff_time.png'\ .format(self._trace.plots_dir, self._trace.plots_prefix) pl.savefig(figname, bbox_inches='tight') # Grid: setup stats for gris gs = gridspec.GridSpec(1, 3, height_ratios=[2]) gs.update(wspace=0.1, hspace=0.1) fig = plt.figure(figsize=(16, 4)) # Plot: usage, capacity and energy distributuions axes = plt.subplot(gs[0, 0]) df[['usage_delta']].hist(ax=axes, bins=60) axes = plt.subplot(gs[0, 1]) df[['cap_delta']].hist(ax=axes, bins=60) axes = plt.subplot(gs[0, 2]) df[['nrg_delta']].hist(ax=axes, bins=60) # Save generated plots into datadir figname = '{}/{}ediff_stats.png'\ .format(self._trace.plots_dir, self._trace.plots_prefix) pl.savefig(figname, bbox_inches='tight') def plotEDiffSpace(self, tasks=None, min_usage_delta=None, max_usage_delta=None, min_cap_delta=None, max_cap_delta=None, min_nrg_delta=None, max_nrg_delta=None, min_nrg_diff=None, max_nrg_diff=None): """ Plot energy_diff()-related signals on the Performance-Energy space (PxE). """ if not self._trace.hasEvents('sched_energy_diff'): self._log.warning('Event [sched_energy_diff] not found, plot DISABLED!') return df = self._dfg_trace_event('sched_energy_diff') # Filter on 'tasks' if tasks is not None: self._log.info('Plotting EDiff data just for task(s) [%s]', tasks) df = df[df['comm'].isin(tasks)] # Filter on 'usage_delta' if min_usage_delta is not None: self._log.info('Plotting EDiff data just with minimum ' 'usage_delta of [%d]', min_usage_delta) df = df[abs(df['usage_delta']) >= min_usage_delta] if max_usage_delta is not None: self._log.info('Plotting EDiff data just with maximum ' 'usage_delta of [%d]', max_usage_delta) df = df[abs(df['usage_delta']) <= max_usage_delta] # Filter on 'cap_delta' if min_cap_delta is not None: self._log.info('Plotting EDiff data just with minimum ' 'cap_delta of [%d]', min_cap_delta) df = df[abs(df['cap_delta']) >= min_cap_delta] if max_cap_delta is not None: self._log.info('Plotting EDiff data just with maximum ' 'cap_delta of [%d]', max_cap_delta) df = df[abs(df['cap_delta']) <= max_cap_delta] # Filter on 'nrg_delta' if min_nrg_delta is not None: self._log.info('Plotting EDiff data just with minimum ' 'nrg_delta of [%d]', min_nrg_delta) df = df[abs(df['nrg_delta']) >= min_nrg_delta] if max_nrg_delta is not None: self._log.info('Plotting EDiff data just with maximum ' 'nrg_delta of [%d]', max_nrg_delta) df = df[abs(df['nrg_delta']) <= max_nrg_delta] # Filter on 'nrg_diff' if min_nrg_diff is not None: self._log.info('Plotting EDiff data just with minimum ' 'nrg_diff of [%d]', min_nrg_diff) df = df[abs(df['nrg_diff']) >= min_nrg_diff] if max_nrg_diff is not None: self._log.info('Plotting EDiff data just with maximum ' 'nrg_diff of [%d]', max_nrg_diff) df = df[abs(df['nrg_diff']) <= max_nrg_diff] # Grid: setup grid for P-E space gs = gridspec.GridSpec(1, 2, height_ratios=[2]) gs.update(wspace=0.1, hspace=0.1) fig = plt.figure(figsize=(16, 8)) # Get min-max of each axes x_min = df.nrg_diff_pct.min() x_max = df.nrg_diff_pct.max() y_min = df.cap_delta.min() y_max = df.cap_delta.max() axes_min = min(x_min, y_min) axes_max = max(x_max, y_max) # # Tag columns by usage_delta # ccol = df.usage_delta # df['usage_delta_group'] = np.select( # [ccol < 150, ccol < 400, ccol < 600], # ['< 150', '< 400', '< 600'], '>= 600') # # # Tag columns by nrg_payoff # ccol = df.nrg_payoff # df['nrg_payoff_group'] = np.select( # [ccol > 2e9, ccol > 0, ccol > -2e9], # ['Optimal Accept', 'SchedTune Accept', 'SchedTune Reject'], # 'Suboptimal Reject') # Plot: per usage_delta values axes = plt.subplot(gs[0, 0]) for color, label in zip('bgyr', ['< 150', '< 400', '< 600', '>= 600']): subset = df[df.usage_delta_group == label] if len(subset) == 0: continue plt.scatter(subset.nrg_diff_pct, subset.cap_delta, s=subset.usage_delta, c=color, label='task_usage ' + str(label), axes=axes) # Plot space axes plt.plot((0, 0), (-1025, 1025), 'y--', axes=axes) plt.plot((-1025, 1025), (0, 0), 'y--', axes=axes) # # Perf cuts # plt.plot((0, 100), (0, 100*delta_pb), 'b--', # label='PB (Perf Boost)') # plt.plot((0, -100), (0, -100*delta_pc), 'r--', # label='PC (Perf Constraint)') # # # Perf boost setups # for y in range(0,6): # plt.plot((0, 500), (0,y*100), 'g:') # for x in range(0,5): # plt.plot((0, x*100), (0,500), 'g:') axes.legend(loc=4, borderpad=1) plt.xlim(1.1*axes_min, 1.1*axes_max) plt.ylim(1.1*axes_min, 1.1*axes_max) # axes.title('Performance-Energy Space') axes.set_xlabel('Energy diff [%]') axes.set_ylabel('Capacity diff [%]') # Plot: per usage_delta values axes = plt.subplot(gs[0, 1]) colors_labels = zip('gbyr', ['Optimal Accept', 'SchedTune Accept', 'SchedTune Reject', 'Suboptimal Reject']) for color, label in colors_labels: subset = df[df.nrg_payoff_group == label] if len(subset) == 0: continue plt.scatter(subset.nrg_diff_pct, subset.cap_delta, s=60, c=color, marker='+', label='{} Region'.format(label), axes=axes) # s=subset.usage_delta, # Plot space axes plt.plot((0, 0), (-1025, 1025), 'y--', axes=axes) plt.plot((-1025, 1025), (0, 0), 'y--', axes=axes) # # Perf cuts # plt.plot((0, 100), (0, 100*delta_pb), 'b--', # label='PB (Perf Boost)') # plt.plot((0, -100), (0, -100*delta_pc), 'r--', # label='PC (Perf Constraint)') # # # Perf boost setups # for y in range(0,6): # plt.plot((0, 500), (0,y*100), 'g:') # for x in range(0,5): # plt.plot((0, x*100), (0,500), 'g:') axes.legend(loc=4, borderpad=1) plt.xlim(1.1*axes_min, 1.1*axes_max) plt.ylim(1.1*axes_min, 1.1*axes_max) # axes.title('Performance-Energy Space') axes.set_xlabel('Energy diff [%]') axes.set_ylabel('Capacity diff [%]') plt.title('Performance-Energy Space') # Save generated plots into datadir figname = '{}/{}ediff_space.png'\ .format(self._trace.plots_dir, self._trace.plots_prefix) pl.savefig(figname, bbox_inches='tight') def plotSchedTuneConf(self): """ Plot the configuration of SchedTune. """ if not self._trace.hasEvents('sched_tune_config'): self._log.warning('Event [sched_tune_config] not found, plot DISABLED!') return # Grid gs = gridspec.GridSpec(2, 1, height_ratios=[4, 1]) gs.update(wspace=0.1, hspace=0.1) # Figure plt.figure(figsize=(16, 2*6)) plt.suptitle("SchedTune Configuration", y=.97, fontsize=16, horizontalalignment='center') # Plot: Margin axes = plt.subplot(gs[0, 0]) axes.set_title('Margin') data = self._dfg_trace_event('sched_tune_config')[['margin']] data.plot(ax=axes, drawstyle='steps-post', style=['b']) axes.set_ylim(0, 110) axes.set_xlim(self._trace.x_min, self._trace.x_max) axes.xaxis.set_visible(False) # Plot: Boost mode axes = plt.subplot(gs[1, 0]) axes.set_title('Boost mode') data = self._dfg_trace_event('sched_tune_config')[['boostmode']] data.plot(ax=axes, drawstyle='steps-post') axes.set_ylim(0, 4) axes.set_xlim(self._trace.x_min, self._trace.x_max) axes.xaxis.set_visible(True) # Save generated plots into datadir figname = '{}/{}schedtune_conf.png'\ .format(self._trace.plots_dir, self._trace.plots_prefix) pl.savefig(figname, bbox_inches='tight') # vim :set tabstop=4 shiftwidth=4 expandtab