blob: c38973e1d38e6d2ce4cabaffb8003625dc8424db [file] [log] [blame]
# 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.
#
import argparse
import fnmatch as fnm
import json
import math
import numpy as np
import os
import re
import sys
import logging
from collections import defaultdict
from colors import TestColors
from results import Results
# By default compare all the possible combinations
DEFAULT_COMPARE = [(r'base_', r'test_')]
class Report(object):
def __init__(self, results_dir, compare=None, formats=['relative']):
self.results_json = results_dir + '/results.json'
self.results = {}
self.compare = []
# Setup logging
self._log = logging.getLogger('Report')
# Parse results (if required)
if not os.path.isfile(self.results_json):
Results(results_dir)
# Load results from file (if already parsed)
self._log.info('Load results from [%s]...',
self.results_json)
with open(self.results_json) as infile:
self.results = json.load(infile)
# Setup configuration comparisons
if compare is None:
compare = DEFAULT_COMPARE
self._log.warning('Comparing all the possible combination')
for (base_rexp, test_rexp) in compare:
self._log.info('Configured regexps for comparisions '
'(bases , tests): (%s, %s)',
base_rexp, test_rexp)
base_rexp = re.compile(base_rexp, re.DOTALL)
test_rexp = re.compile(test_rexp, re.DOTALL)
self.compare.append((base_rexp, test_rexp))
# Report all supported workload classes
self.__rtapp_report(formats)
self.__default_report(formats)
############################### REPORT RTAPP ###############################
def __rtapp_report(self, formats):
if 'rtapp' not in self.results.keys():
self._log.debug('No RTApp workloads to report')
return
self._log.debug('Reporting RTApp workloads')
# Setup lables depending on requested report
if 'absolute' in formats:
nrg_lable = 'Energy Indexes (Absolute)'
prf_lable = 'Performance Indexes (Absolute)'
self._log.info('')
self._log.info('Absolute comparisions:')
print ''
else:
nrg_lable = 'Energy Indexes (Relative)'
prf_lable = 'Performance Indexes (Relative)'
self._log.info('')
self._log.info('Relative comparisions:')
print ''
# Dump headers
print '{:13s} {:20s} |'\
' {:33s} | {:54s} |'\
.format('Test Id', 'Comparision',
nrg_lable, prf_lable)
print '{:13s} {:20s} |'\
' {:>10s} {:>10s} {:>10s} |'\
' {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} |'\
.format('', '',
'LITTLE', 'big', 'Total',
'PerfIndex', 'NegSlacks', 'EDP1', 'EDP2', 'EDP3')
# For each test
_results = self.results['rtapp']
for tid in sorted(_results.keys()):
new_test = True
# For each configuration...
for base_idx in sorted(_results[tid].keys()):
# Which matches at least on base regexp
for (base_rexp, test_rexp) in self.compare:
if not base_rexp.match(base_idx):
continue
# Look for a configuration which matches the test regexp
for test_idx in sorted(_results[tid].keys()):
if test_idx == base_idx:
continue
if new_test:
print '{:-<37s}+{:-<35s}+{:-<56s}+'\
.format('','', '')
self.__rtapp_reference(tid, base_idx)
new_test = False
if test_rexp.match(test_idx) == None:
continue
self.__rtapp_compare(tid, base_idx, test_idx, formats)
print ''
def __rtapp_reference(self, tid, base_idx):
_results = self.results['rtapp']
self._log.debug('Test %s: compare against [%s] base',
tid, base_idx)
res_line = '{0:12s}: {1:22s} | '.format(tid, base_idx)
# Dump all energy metrics
for cpus in ['LITTLE', 'big', 'Total']:
res_base = _results[tid][base_idx]['energy'][cpus]['avg']
# Dump absolute values
res_line += ' {0:10.3f}'.format(res_base)
res_line += ' |'
# If available, dump also performance results
if 'performance' not in _results[tid][base_idx].keys():
print res_line
return
for pidx in ['perf_avg', 'slack_pct', 'edp1', 'edp2', 'edp3']:
res_base = _results[tid][base_idx]['performance'][pidx]['avg']
self._log.debug('idx: %s, base: %s', pidx, res_base)
if pidx in ['perf_avg']:
res_line += ' {0:s}'.format(TestColors.rate(res_base))
continue
if pidx in ['slack_pct']:
res_line += ' {0:s}'.format(
TestColors.rate(res_base, positive_is_good = False))
continue
if 'edp' in pidx:
res_line += ' {0:10.2e}'.format(res_base)
continue
res_line += ' |'
print res_line
def __rtapp_compare(self, tid, base_idx, test_idx, formats):
_results = self.results['rtapp']
self._log.debug('Test %s: compare %s with %s',
tid, base_idx, test_idx)
res_line = '{0:12s}: {1:20s} | '.format(tid, test_idx)
# Dump all energy metrics
for cpus in ['LITTLE', 'big', 'Total']:
res_base = _results[tid][base_idx]['energy'][cpus]['avg']
res_test = _results[tid][test_idx]['energy'][cpus]['avg']
speedup_cnt = res_test - res_base
if 'absolute' in formats:
res_line += ' {0:10.2f}'.format(speedup_cnt)
else:
speedup_pct = 0
if res_base != 0:
speedup_pct = 100.0 * speedup_cnt / res_base
res_line += ' {0:s}'\
.format(TestColors.rate(
speedup_pct,
positive_is_good = False))
res_line += ' |'
# If available, dump also performance results
if 'performance' not in _results[tid][base_idx].keys():
print res_line
return
for pidx in ['perf_avg', 'slack_pct', 'edp1', 'edp2', 'edp3']:
res_base = _results[tid][base_idx]['performance'][pidx]['avg']
res_test = _results[tid][test_idx]['performance'][pidx]['avg']
self._log.debug('idx: %s, base: %s, test: %s',
pidx, res_base, res_test)
if pidx in ['perf_avg']:
res_line += ' {0:s}'.format(TestColors.rate(res_test))
continue
if pidx in ['slack_pct']:
res_line += ' {0:s}'.format(
TestColors.rate(res_test, positive_is_good = False))
continue
# Compute difference base-vs-test
if 'edp' in pidx:
speedup_cnt = res_base - res_test
if 'absolute':
res_line += ' {0:10.2e}'.format(speedup_cnt)
else:
res_line += ' {0:s}'.format(TestColors.rate(speedup_pct))
res_line += ' |'
print res_line
############################### REPORT DEFAULT #############################
def __default_report(self, formats):
# Build list of workload types which can be rendered using the default parser
wtypes = []
for supported_wtype in DEFAULT_WTYPES:
if supported_wtype in self.results.keys():
wtypes.append(supported_wtype)
if len(wtypes) == 0:
self._log.debug('No Default workloads to report')
return
self._log.debug('Reporting Default workloads')
# Setup lables depending on requested report
if 'absolute' in formats:
nrg_lable = 'Energy Indexes (Absolute)'
prf_lable = 'Performance Indexes (Absolute)'
self._log.info('')
self._log.info('Absolute comparisions:')
print ''
else:
nrg_lable = 'Energy Indexes (Relative)'
prf_lable = 'Performance Indexes (Relative)'
self._log.info('')
self._log.info('Relative comparisions:')
print ''
# Dump headers
print '{:9s} {:20s} |'\
' {:33s} | {:54s} |'\
.format('Test Id', 'Comparision',
nrg_lable, prf_lable)
print '{:9s} {:20s} |'\
' {:>10s} {:>10s} {:>10s} |'\
' {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} |'\
.format('', '',
'LITTLE', 'big', 'Total',
'Perf', 'CTime', 'EDP1', 'EDP2', 'EDP3')
# For each default test
for wtype in wtypes:
_results = self.results[wtype]
for tid in sorted(_results.keys()):
new_test = True
# For each configuration...
for base_idx in sorted(_results[tid].keys()):
# Which matches at least on base regexp
for (base_rexp, test_rexp) in self.compare:
if not base_rexp.match(base_idx):
continue
# Look for a configuration which matches the test regexp
for test_idx in sorted(_results[tid].keys()):
if test_idx == base_idx:
continue
if new_test:
print '{:-<37s}+{:-<35s}+{:-<56s}+'\
.format('','', '')
new_test = False
if not test_rexp.match(test_idx):
continue
self.__default_compare(wtype, tid, base_idx, test_idx, formats)
print ''
def __default_compare(self, wtype, tid, base_idx, test_idx, formats):
_results = self.results[wtype]
self._log.debug('Test %s: compare %s with %s',
tid, base_idx, test_idx)
res_comp = '{0:s} vs {1:s}'.format(test_idx, base_idx)
res_line = '{0:8s}: {1:22s} | '.format(tid, res_comp)
# Dump all energy metrics
for cpus in ['LITTLE', 'big', 'Total']:
# If either base of test have a 0 MAX energy, this measn that
# energy has not been collected
base_max = _results[tid][base_idx]['energy'][cpus]['max']
test_max = _results[tid][test_idx]['energy'][cpus]['max']
if base_max == 0 or test_max == 0:
res_line += ' {0:10s}'.format('NA')
continue
# Otherwise, report energy values
res_base = _results[tid][base_idx]['energy'][cpus]['avg']
res_test = _results[tid][test_idx]['energy'][cpus]['avg']
speedup_cnt = res_test - res_base
if 'absolute' in formats:
res_line += ' {0:10.2f}'.format(speedup_cnt)
else:
speedup_pct = 100.0 * speedup_cnt / res_base
res_line += ' {0:s}'\
.format(TestColors.rate(
speedup_pct,
positive_is_good = False))
res_line += ' |'
# If available, dump also performance results
if 'performance' not in _results[tid][base_idx].keys():
print res_line
return
for pidx in ['perf_avg', 'ctime_avg', 'edp1', 'edp2', 'edp3']:
res_base = _results[tid][base_idx]['performance'][pidx]['avg']
res_test = _results[tid][test_idx]['performance'][pidx]['avg']
self._log.debug('idx: %s, base: %s, test: %s',
pidx, res_base, res_test)
# Compute difference base-vs-test
speedup_cnt = 0
if res_base != 0:
if pidx in ['perf_avg']:
speedup_cnt = res_test - res_base
else:
speedup_cnt = res_base - res_test
# Compute speedup if required
speedup_pct = 0
if 'absolute' in formats:
if 'edp' in pidx:
res_line += ' {0:10.2e}'.format(speedup_cnt)
else:
res_line += ' {0:10.2f}'.format(speedup_cnt)
else:
if res_base != 0:
if pidx in ['perf_avg']:
# speedup_pct = 100.0 * speedup_cnt / res_base
speedup_pct = speedup_cnt
else:
speedup_pct = 100.0 * speedup_cnt / res_base
res_line += ' {0:s}'.format(TestColors.rate(speedup_pct))
res_line += ' |'
print res_line
# List of workload types which can be parsed using the default test parser
DEFAULT_WTYPES = ['perf_bench_messaging', 'perf_bench_pipe']
#vim :set tabstop=4 shiftwidth=4 expandtab