First patch from the series simplifies how we dump and post-process statistics. I switched to use ';' separated values instead of a complex human language. Changes to analyze_brprob.py add possibility to filter out dominant edges. Having that one can see how predictor values change if a dominant edge is ignored.
Martin
>From ef3c162961f3599ee65e87ecde5138d9f37f0221 Mon Sep 17 00:00:00 2001 From: marxin <mli...@suse.cz> Date: Thu, 21 Dec 2017 17:19:13 +0100 Subject: [PATCH 1/5] Fix usage of analyze_brprob.py script. contrib/ChangeLog: 2017-12-21 Martin Liska <mli...@suse.cz> * analyze_brprob.py: Support new format that can be easily parsed. Add new column to report. gcc/ChangeLog: 2017-12-21 Martin Liska <mli...@suse.cz> * predict.c (dump_prediction): Add new format for analyze_brprob.py script which is enabled with -details suboption. * profile-count.h (precise_p): New function. --- contrib/analyze_brprob.py | 103 ++++++++++++++++++++++++++++++++-------------- gcc/predict.c | 13 ++++++ gcc/profile-count.h | 5 +++ 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/contrib/analyze_brprob.py b/contrib/analyze_brprob.py index e03d1da1cde..de5f474d629 100755 --- a/contrib/analyze_brprob.py +++ b/contrib/analyze_brprob.py @@ -71,6 +71,7 @@ from math import * counter_aggregates = set(['combined', 'first match', 'DS theory', 'no prediction']) +hot_threshold = 10 def percentage(a, b): return 100.0 * a / b @@ -131,47 +132,87 @@ class PredictDefFile: with open(self.path, 'w+') as f: for l in modified_lines: f.write(l + '\n') +class Heuristics: + def __init__(self, count, hits, fits): + self.count = count + self.hits = hits + self.fits = fits class Summary: def __init__(self, name): self.name = name - self.branches = 0 - self.successfull_branches = 0 - self.count = 0 - self.hits = 0 - self.fits = 0 + self.edges= [] + + def branches(self): + return len(self.edges) + + def hits(self): + return sum([x.hits for x in self.edges]) + + def fits(self): + return sum([x.fits for x in self.edges]) + + def count(self): + return sum([x.count for x in self.edges]) + + def successfull_branches(self): + return len([x for x in self.edges if 2 * x.hits >= x.count]) def get_hitrate(self): - return 100.0 * self.hits / self.count + return 100.0 * self.hits() / self.count() def get_branch_hitrate(self): - return 100.0 * self.successfull_branches / self.branches + return 100.0 * self.successfull_branches() / self.branches() def count_formatted(self): - v = self.count + v = self.count() for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']: if v < 1000: return "%3.2f%s" % (v, unit) v /= 1000.0 return "%.1f%s" % (v, 'Y') + def count(self): + return sum([x.count for x in self.edges]) + def print(self, branches_max, count_max, predict_def): + # filter out most hot edges (if requested) + self.edges = sorted(self.edges, reverse = True, key = lambda x: x.count) + if args.coverage_threshold != None: + threshold = args.coverage_threshold * self.count() / 100 + edges = [x for x in self.edges if x.count < threshold] + if len(edges) != 0: + self.edges = edges + predicted_as = None if predict_def != None and self.name in predict_def.predictors: predicted_as = predict_def.predictors[self.name] print('%-40s %8i %5.1f%% %11.2f%% %7.2f%% / %6.2f%% %14i %8s %5.1f%%' % - (self.name, self.branches, - percentage(self.branches, branches_max), + (self.name, self.branches(), + percentage(self.branches(), branches_max), self.get_branch_hitrate(), self.get_hitrate(), - percentage(self.fits, self.count), - self.count, self.count_formatted(), - percentage(self.count, count_max)), end = '') + percentage(self.fits(), self.count()), + self.count(), self.count_formatted(), + percentage(self.count(), count_max)), end = '') if predicted_as != None: print('%12i%% %5.1f%%' % (predicted_as, self.get_hitrate() - predicted_as), end = '') + else: + print(' ' * 20, end = '') + + # print details about the most important edges + if args.coverage_threshold == None: + edges = [x for x in self.edges[:100] if x.count * hot_threshold > self.count()] + if args.verbose: + for c in edges: + r = 100.0 * c.count / self.count() + print(' %.0f%%:%d' % (r, c.count), end = '') + elif len(edges) > 0: + print(' %0.0f%%:%d' % (100.0 * sum([x.count for x in edges]) / self.count(), len(edges)), end = '') + print() class Profile: @@ -185,33 +226,29 @@ class Profile: self.heuristics[name] = Summary(name) s = self.heuristics[name] - s.branches += 1 - s.count += count if prediction < 50: hits = count - hits remaining = count - hits - if hits >= remaining: - s.successfull_branches += 1 + fits = max(hits, remaining) - s.hits += hits - s.fits += max(hits, remaining) + s.edges.append(Heuristics(count, hits, fits)) def add_loop_niter(self, niter): if niter > 0: self.niter_vector.append(niter) def branches_max(self): - return max([v.branches for k, v in self.heuristics.items()]) + return max([v.branches() for k, v in self.heuristics.items()]) def count_max(self): - return max([v.count for k, v in self.heuristics.items()]) + return max([v.count() for k, v in self.heuristics.items()]) def print_group(self, sorting, group_name, heuristics, predict_def): count_max = self.count_max() branches_max = self.branches_max() - sorter = lambda x: x.branches + sorter = lambda x: x.branches() if sorting == 'branch-hitrate': sorter = lambda x: x.get_branch_hitrate() elif sorting == 'hitrate': @@ -221,10 +258,10 @@ class Profile: elif sorting == 'name': sorter = lambda x: x.name.lower() - print('%-40s %8s %6s %12s %18s %14s %8s %6s %12s %6s' % + print('%-40s %8s %6s %12s %18s %14s %8s %6s %12s %6s %s' % ('HEURISTICS', 'BRANCHES', '(REL)', 'BR. HITRATE', 'HITRATE', 'COVERAGE', 'COVERAGE', '(REL)', - 'predict.def', '(REL)')) + 'predict.def', '(REL)', 'HOT branches (>%d%%)' % hot_threshold)) for h in sorted(heuristics, key = sorter): h.print(branches_max, count_max, predict_def) @@ -266,19 +303,23 @@ parser.add_argument('-s', '--sorting', dest = 'sorting', parser.add_argument('-d', '--def-file', help = 'path to predict.def') parser.add_argument('-w', '--write-def-file', action = 'store_true', help = 'Modify predict.def file in order to set new numbers') +parser.add_argument('-c', '--coverage-threshold', type = int, + help = 'Ignore edges that have percentage coverage >= coverage-threshold') +parser.add_argument('-v', '--verbose', action = 'store_true', help = 'Print verbose informations') args = parser.parse_args() profile = Profile(args.dump_file) -r = re.compile(' (.*) heuristics( of edge [0-9]*->[0-9]*)?( \\(.*\\))?: (.*)%.*exec ([0-9]*) hit ([0-9]*)') loop_niter_str = ';; profile-based iteration count: ' + for l in open(args.dump_file): - m = r.match(l) - if m != None and m.group(3) == None: - name = m.group(1) - prediction = float(m.group(4)) - count = int(m.group(5)) - hits = int(m.group(6)) + if l.startswith(';;heuristics;'): + parts = l.strip().split(';') + assert len(parts) == 8 + name = parts[3] + prediction = float(parts[6]) + count = int(parts[4]) + hits = int(parts[5]) profile.add(name, prediction, count, hits) elif l.startswith(loop_niter_str): diff --git a/gcc/predict.c b/gcc/predict.c index 9cea90d1063..3ac18a2c5f2 100644 --- a/gcc/predict.c +++ b/gcc/predict.c @@ -747,6 +747,19 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability, } fprintf (file, "\n"); + + /* Print output that be easily read by analyze_brprob.py script. We are + interested only in counts that are read from GCDA files. */ + if (dump_file && (dump_flags & TDF_DETAILS) + && bb->count.precise_p () + && reason == REASON_NONE) + { + gcc_assert (e->count ().precise_p ()); + fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n", + predictor_info[predictor].name, + bb->count.to_gcov_type (), e->count ().to_gcov_type (), + probability * 100.0 / REG_BR_PROB_BASE); + } } /* Return true if STMT is known to be unlikely executed. */ diff --git a/gcc/profile-count.h b/gcc/profile-count.h index 3c5f720ee81..3a37b1293e5 100644 --- a/gcc/profile-count.h +++ b/gcc/profile-count.h @@ -689,6 +689,11 @@ public: { return !initialized_p () || m_quality >= profile_guessed_global0; } + /* Return true if quality of profile is precise. */ + bool precise_p () const + { + return m_quality == profile_precise; + } /* When merging basic blocks, the two different profile counts are unified. Return true if this can be done without losing info about profile. -- 2.14.3