1# SPDX-License-Identifier: Apache-2.0 2# 3# Copyright (C) 2015, ARM Limited and contributors. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may 6# not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18""" EAS-specific Analysis Module """ 19 20import matplotlib.gridspec as gridspec 21import matplotlib.pyplot as plt 22import pylab as pl 23 24from analysis_module import AnalysisModule 25 26 27class EasAnalysis(AnalysisModule): 28 """ 29 Support for EAS signals anaysis 30 31 :param trace: input Trace object 32 :type trace: :mod:`libs.utils.Trace` 33 """ 34 35 def __init__(self, trace): 36 super(EasAnalysis, self).__init__(trace) 37 38############################################################################### 39# DataFrame Getter Methods 40############################################################################### 41 42 43############################################################################### 44# Plotting Methods 45############################################################################### 46 47 def plotEDiffTime(self, tasks=None, 48 min_usage_delta=None, max_usage_delta=None, 49 min_cap_delta=None, max_cap_delta=None, 50 min_nrg_delta=None, max_nrg_delta=None, 51 min_nrg_diff=None, max_nrg_diff=None): 52 """ 53 Plot energy_diff()-related signals on time axes. 54 """ 55 if not self._trace.hasEvents('sched_energy_diff'): 56 self._log.warning('Event [sched_energy_diff] not found, plot DISABLED!') 57 return 58 df = self._dfg_trace_event('sched_energy_diff') 59 60 # Filter on 'tasks' 61 if tasks is not None: 62 self._log.info('Plotting EDiff data just for task(s) [%s]', tasks) 63 df = df[df['comm'].isin(tasks)] 64 65 # Filter on 'usage_delta' 66 if min_usage_delta is not None: 67 self._log.info('Plotting EDiff data just with minimum ' 68 'usage_delta of [%d]', min_usage_delta) 69 df = df[abs(df['usage_delta']) >= min_usage_delta] 70 if max_usage_delta is not None: 71 self._log.info('Plotting EDiff data just with maximum ' 72 'usage_delta of [%d]', max_usage_delta) 73 df = df[abs(df['usage_delta']) <= max_usage_delta] 74 75 # Filter on 'cap_delta' 76 if min_cap_delta is not None: 77 self._log.info('Plotting EDiff data just with minimum ' 78 'cap_delta of [%d]', min_cap_delta) 79 df = df[abs(df['cap_delta']) >= min_cap_delta] 80 if max_cap_delta is not None: 81 self._log.info('Plotting EDiff data just with maximum ' 82 'cap_delta of [%d]', max_cap_delta) 83 df = df[abs(df['cap_delta']) <= max_cap_delta] 84 85 # Filter on 'nrg_delta' 86 if min_nrg_delta is not None: 87 self._log.info('Plotting EDiff data just with minimum ' 88 'nrg_delta of [%d]', min_nrg_delta) 89 df = df[abs(df['nrg_delta']) >= min_nrg_delta] 90 if max_nrg_delta is not None: 91 self._log.info('Plotting EDiff data just with maximum ' 92 'nrg_delta of [%d]', max_nrg_delta) 93 df = df[abs(df['nrg_delta']) <= max_nrg_delta] 94 95 # Filter on 'nrg_diff' 96 if min_nrg_diff is not None: 97 self._log.info('Plotting EDiff data just with minimum ' 98 'nrg_diff of [%d]', min_nrg_diff) 99 df = df[abs(df['nrg_diff']) >= min_nrg_diff] 100 if max_nrg_diff is not None: 101 self._log.info('Plotting EDiff data just with maximum ' 102 'nrg_diff of [%d]', max_nrg_diff) 103 df = df[abs(df['nrg_diff']) <= max_nrg_diff] 104 105 # Grid: setup stats for gris 106 gs = gridspec.GridSpec(4, 3, height_ratios=[2, 4, 2, 4]) 107 gs.update(wspace=0.1, hspace=0.1) 108 109 # Configure plot 110 fig = plt.figure(figsize=(16, 8*2+4*2+2)) 111 plt.suptitle("EnergyDiff Data", 112 y=.92, fontsize=16, horizontalalignment='center') 113 114 # Plot1: src and dst CPUs 115 axes = plt.subplot(gs[0, :]) 116 axes.set_title('Source and Destination CPUs') 117 df[['src_cpu', 'dst_cpu']].plot(ax=axes, style=['bo', 'r+']) 118 axes.set_ylim(-1, self._platform['cpus_count']+1) 119 axes.set_xlim(self._trace.x_min, self._trace.x_max) 120 axes.grid(True) 121 axes.set_xticklabels([]) 122 axes.set_xlabel('') 123 self._trace.analysis.status.plotOverutilized(axes) 124 125 # Plot2: energy and capacity variations 126 axes = plt.subplot(gs[1, :]) 127 axes.set_title('Energy vs Capacity Variations') 128 129 colors_labels = zip('gbyr', ['Optimal Accept', 'SchedTune Accept', 130 'SchedTune Reject', 'Suboptimal Reject']) 131 for color, label in colors_labels: 132 subset = df[df.nrg_payoff_group == label] 133 if len(subset) == 0: 134 continue 135 subset[['nrg_diff_pct']].plot(ax=axes, style=[color+'o']) 136 axes.set_xlim(self._trace.x_min, self._trace.x_max) 137 axes.set_yscale('symlog') 138 axes.grid(True) 139 axes.set_xticklabels([]) 140 axes.set_xlabel('') 141 self._trace.analysis.status.plotOverutilized(axes) 142 143 # Plot3: energy payoff 144 axes = plt.subplot(gs[2, :]) 145 axes.set_title('Energy Payoff Values') 146 for color, label in colors_labels: 147 subset = df[df.nrg_payoff_group == label] 148 if len(subset) == 0: 149 continue 150 subset[['nrg_payoff']].plot(ax=axes, style=[color+'o']) 151 axes.set_xlim(self._trace.x_min, self._trace.x_max) 152 axes.set_yscale('symlog') 153 axes.grid(True) 154 axes.set_xticklabels([]) 155 axes.set_xlabel('') 156 self._trace.analysis.status.plotOverutilized(axes) 157 158 # Plot4: energy deltas (kernel and host computed values) 159 axes = plt.subplot(gs[3, :]) 160 axes.set_title('Energy Deltas Values') 161 df[['nrg_delta', 'nrg_diff_pct']].plot(ax=axes, style=['ro', 'b+']) 162 axes.set_xlim(self._trace.x_min, self._trace.x_max) 163 axes.grid(True) 164 self._trace.analysis.status.plotOverutilized(axes) 165 166 # Save generated plots into datadir 167 figname = '{}/{}ediff_time.png'\ 168 .format(self._trace.plots_dir, self._trace.plots_prefix) 169 pl.savefig(figname, bbox_inches='tight') 170 171 # Grid: setup stats for gris 172 gs = gridspec.GridSpec(1, 3, height_ratios=[2]) 173 gs.update(wspace=0.1, hspace=0.1) 174 175 fig = plt.figure(figsize=(16, 4)) 176 177 # Plot: usage, capacity and energy distributuions 178 axes = plt.subplot(gs[0, 0]) 179 df[['usage_delta']].hist(ax=axes, bins=60) 180 axes = plt.subplot(gs[0, 1]) 181 df[['cap_delta']].hist(ax=axes, bins=60) 182 axes = plt.subplot(gs[0, 2]) 183 df[['nrg_delta']].hist(ax=axes, bins=60) 184 185 # Save generated plots into datadir 186 figname = '{}/{}ediff_stats.png'\ 187 .format(self._trace.plots_dir, self._trace.plots_prefix) 188 pl.savefig(figname, bbox_inches='tight') 189 190 def plotEDiffSpace(self, tasks=None, 191 min_usage_delta=None, max_usage_delta=None, 192 min_cap_delta=None, max_cap_delta=None, 193 min_nrg_delta=None, max_nrg_delta=None, 194 min_nrg_diff=None, max_nrg_diff=None): 195 """ 196 Plot energy_diff()-related signals on the Performance-Energy space 197 (PxE). 198 """ 199 if not self._trace.hasEvents('sched_energy_diff'): 200 self._log.warning('Event [sched_energy_diff] not found, plot DISABLED!') 201 return 202 df = self._dfg_trace_event('sched_energy_diff') 203 204 # Filter on 'tasks' 205 if tasks is not None: 206 self._log.info('Plotting EDiff data just for task(s) [%s]', tasks) 207 df = df[df['comm'].isin(tasks)] 208 209 # Filter on 'usage_delta' 210 if min_usage_delta is not None: 211 self._log.info('Plotting EDiff data just with minimum ' 212 'usage_delta of [%d]', min_usage_delta) 213 df = df[abs(df['usage_delta']) >= min_usage_delta] 214 if max_usage_delta is not None: 215 self._log.info('Plotting EDiff data just with maximum ' 216 'usage_delta of [%d]', max_usage_delta) 217 df = df[abs(df['usage_delta']) <= max_usage_delta] 218 219 # Filter on 'cap_delta' 220 if min_cap_delta is not None: 221 self._log.info('Plotting EDiff data just with minimum ' 222 'cap_delta of [%d]', min_cap_delta) 223 df = df[abs(df['cap_delta']) >= min_cap_delta] 224 if max_cap_delta is not None: 225 self._log.info('Plotting EDiff data just with maximum ' 226 'cap_delta of [%d]', max_cap_delta) 227 df = df[abs(df['cap_delta']) <= max_cap_delta] 228 229 # Filter on 'nrg_delta' 230 if min_nrg_delta is not None: 231 self._log.info('Plotting EDiff data just with minimum ' 232 'nrg_delta of [%d]', min_nrg_delta) 233 df = df[abs(df['nrg_delta']) >= min_nrg_delta] 234 if max_nrg_delta is not None: 235 self._log.info('Plotting EDiff data just with maximum ' 236 'nrg_delta of [%d]', max_nrg_delta) 237 df = df[abs(df['nrg_delta']) <= max_nrg_delta] 238 239 # Filter on 'nrg_diff' 240 if min_nrg_diff is not None: 241 self._log.info('Plotting EDiff data just with minimum ' 242 'nrg_diff of [%d]', min_nrg_diff) 243 df = df[abs(df['nrg_diff']) >= min_nrg_diff] 244 if max_nrg_diff is not None: 245 self._log.info('Plotting EDiff data just with maximum ' 246 'nrg_diff of [%d]', max_nrg_diff) 247 df = df[abs(df['nrg_diff']) <= max_nrg_diff] 248 249 # Grid: setup grid for P-E space 250 gs = gridspec.GridSpec(1, 2, height_ratios=[2]) 251 gs.update(wspace=0.1, hspace=0.1) 252 253 fig = plt.figure(figsize=(16, 8)) 254 255 # Get min-max of each axes 256 x_min = df.nrg_diff_pct.min() 257 x_max = df.nrg_diff_pct.max() 258 y_min = df.cap_delta.min() 259 y_max = df.cap_delta.max() 260 axes_min = min(x_min, y_min) 261 axes_max = max(x_max, y_max) 262 263 # # Tag columns by usage_delta 264 # ccol = df.usage_delta 265 # df['usage_delta_group'] = np.select( 266 # [ccol < 150, ccol < 400, ccol < 600], 267 # ['< 150', '< 400', '< 600'], '>= 600') 268 # 269 # # Tag columns by nrg_payoff 270 # ccol = df.nrg_payoff 271 # df['nrg_payoff_group'] = np.select( 272 # [ccol > 2e9, ccol > 0, ccol > -2e9], 273 # ['Optimal Accept', 'SchedTune Accept', 'SchedTune Reject'], 274 # 'Suboptimal Reject') 275 276 # Plot: per usage_delta values 277 axes = plt.subplot(gs[0, 0]) 278 279 for color, label in zip('bgyr', ['< 150', '< 400', '< 600', '>= 600']): 280 subset = df[df.usage_delta_group == label] 281 if len(subset) == 0: 282 continue 283 plt.scatter(subset.nrg_diff_pct, subset.cap_delta, 284 s=subset.usage_delta, 285 c=color, label='task_usage ' + str(label), 286 axes=axes) 287 288 # Plot space axes 289 plt.plot((0, 0), (-1025, 1025), 'y--', axes=axes) 290 plt.plot((-1025, 1025), (0, 0), 'y--', axes=axes) 291 292 # # Perf cuts 293 # plt.plot((0, 100), (0, 100*delta_pb), 'b--', 294 # label='PB (Perf Boost)') 295 # plt.plot((0, -100), (0, -100*delta_pc), 'r--', 296 # label='PC (Perf Constraint)') 297 # 298 # # Perf boost setups 299 # for y in range(0,6): 300 # plt.plot((0, 500), (0,y*100), 'g:') 301 # for x in range(0,5): 302 # plt.plot((0, x*100), (0,500), 'g:') 303 304 axes.legend(loc=4, borderpad=1) 305 306 plt.xlim(1.1*axes_min, 1.1*axes_max) 307 plt.ylim(1.1*axes_min, 1.1*axes_max) 308 309 # axes.title('Performance-Energy Space') 310 axes.set_xlabel('Energy diff [%]') 311 axes.set_ylabel('Capacity diff [%]') 312 313 # Plot: per usage_delta values 314 axes = plt.subplot(gs[0, 1]) 315 316 colors_labels = zip('gbyr', ['Optimal Accept', 'SchedTune Accept', 317 'SchedTune Reject', 'Suboptimal Reject']) 318 for color, label in colors_labels: 319 subset = df[df.nrg_payoff_group == label] 320 if len(subset) == 0: 321 continue 322 plt.scatter(subset.nrg_diff_pct, subset.cap_delta, 323 s=60, 324 c=color, 325 marker='+', 326 label='{} Region'.format(label), 327 axes=axes) 328 # s=subset.usage_delta, 329 330 # Plot space axes 331 plt.plot((0, 0), (-1025, 1025), 'y--', axes=axes) 332 plt.plot((-1025, 1025), (0, 0), 'y--', axes=axes) 333 334 # # Perf cuts 335 # plt.plot((0, 100), (0, 100*delta_pb), 'b--', 336 # label='PB (Perf Boost)') 337 # plt.plot((0, -100), (0, -100*delta_pc), 'r--', 338 # label='PC (Perf Constraint)') 339 # 340 # # Perf boost setups 341 # for y in range(0,6): 342 # plt.plot((0, 500), (0,y*100), 'g:') 343 # for x in range(0,5): 344 # plt.plot((0, x*100), (0,500), 'g:') 345 346 axes.legend(loc=4, borderpad=1) 347 348 plt.xlim(1.1*axes_min, 1.1*axes_max) 349 plt.ylim(1.1*axes_min, 1.1*axes_max) 350 351 # axes.title('Performance-Energy Space') 352 axes.set_xlabel('Energy diff [%]') 353 axes.set_ylabel('Capacity diff [%]') 354 355 plt.title('Performance-Energy Space') 356 357 # Save generated plots into datadir 358 figname = '{}/{}ediff_space.png'\ 359 .format(self._trace.plots_dir, self._trace.plots_prefix) 360 pl.savefig(figname, bbox_inches='tight') 361 362 def plotSchedTuneConf(self): 363 """ 364 Plot the configuration of SchedTune. 365 """ 366 if not self._trace.hasEvents('sched_tune_config'): 367 self._log.warning('Event [sched_tune_config] not found, plot DISABLED!') 368 return 369 # Grid 370 gs = gridspec.GridSpec(2, 1, height_ratios=[4, 1]) 371 gs.update(wspace=0.1, hspace=0.1) 372 373 # Figure 374 plt.figure(figsize=(16, 2*6)) 375 plt.suptitle("SchedTune Configuration", 376 y=.97, fontsize=16, horizontalalignment='center') 377 378 # Plot: Margin 379 axes = plt.subplot(gs[0, 0]) 380 axes.set_title('Margin') 381 data = self._dfg_trace_event('sched_tune_config')[['margin']] 382 data.plot(ax=axes, drawstyle='steps-post', style=['b']) 383 axes.set_ylim(0, 110) 384 axes.set_xlim(self._trace.x_min, self._trace.x_max) 385 axes.xaxis.set_visible(False) 386 387 # Plot: Boost mode 388 axes = plt.subplot(gs[1, 0]) 389 axes.set_title('Boost mode') 390 data = self._dfg_trace_event('sched_tune_config')[['boostmode']] 391 data.plot(ax=axes, drawstyle='steps-post') 392 axes.set_ylim(0, 4) 393 axes.set_xlim(self._trace.x_min, self._trace.x_max) 394 axes.xaxis.set_visible(True) 395 396 # Save generated plots into datadir 397 figname = '{}/{}schedtune_conf.png'\ 398 .format(self._trace.plots_dir, self._trace.plots_prefix) 399 pl.savefig(figname, bbox_inches='tight') 400 401# vim :set tabstop=4 shiftwidth=4 expandtab 402