Added recursive results to perf.py
This adds -P/--propagate and -Z/--depth to perf.py for showing recursive results, making it easy to narrow down on where spikes in performance come from. This ended up being a bit different from stack.py's recursive results, as we end up with different (diminishing) numbers as we descend.
This commit is contained in:
@@ -231,7 +231,7 @@ coverage: $(GCDA)
|
|||||||
perf: $(BENCH_PERF)
|
perf: $(BENCH_PERF)
|
||||||
$(strip ./scripts/perf.py \
|
$(strip ./scripts/perf.py \
|
||||||
$^ $(patsubst %,-F%,$(SRC)) \
|
$^ $(patsubst %,-F%,$(SRC)) \
|
||||||
-scycles \
|
-Scycles \
|
||||||
$(PERFFLAGS))
|
$(PERFFLAGS))
|
||||||
|
|
||||||
.PHONY: summary sizes
|
.PHONY: summary sizes
|
||||||
|
|||||||
+67
-99
@@ -257,6 +257,8 @@ def collect(paths, *,
|
|||||||
f_name = m.group('name')
|
f_name = m.group('name')
|
||||||
elif m.group('file'):
|
elif m.group('file'):
|
||||||
f_file = int(m.group('file'))
|
f_file = int(m.group('file'))
|
||||||
|
if is_func:
|
||||||
|
defs[f_name] = files.get(f_file, '?')
|
||||||
proc.wait()
|
proc.wait()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
if not args.get('verbose'):
|
if not args.get('verbose'):
|
||||||
@@ -303,7 +305,7 @@ def collect(paths, *,
|
|||||||
else:
|
else:
|
||||||
file = os.path.abspath(file)
|
file = os.path.abspath(file)
|
||||||
|
|
||||||
results.append(CodeResult(file, r.function, r.size))
|
results.append(r._replace(file=file))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -393,8 +395,8 @@ def table(Result, results, diff_results=None, *,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -403,129 +405,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
|
|||||||
+64
-98
@@ -383,8 +383,8 @@ def table(Result, results, diff_results=None, *,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -393,129 +393,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
|
|||||||
+67
-99
@@ -257,6 +257,8 @@ def collect(paths, *,
|
|||||||
f_name = m.group('name')
|
f_name = m.group('name')
|
||||||
elif m.group('file'):
|
elif m.group('file'):
|
||||||
f_file = int(m.group('file'))
|
f_file = int(m.group('file'))
|
||||||
|
if is_func:
|
||||||
|
defs[f_name] = files.get(f_file, '?')
|
||||||
proc.wait()
|
proc.wait()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
if not args.get('verbose'):
|
if not args.get('verbose'):
|
||||||
@@ -303,7 +305,7 @@ def collect(paths, *,
|
|||||||
else:
|
else:
|
||||||
file = os.path.abspath(file)
|
file = os.path.abspath(file)
|
||||||
|
|
||||||
results.append(DataResult(file, r.function, r.size))
|
results.append(r._replace(file=file))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -393,8 +395,8 @@ def table(Result, results, diff_results=None, *,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -403,129 +405,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
|
|||||||
+263
-224
@@ -28,6 +28,8 @@ import subprocess as sp
|
|||||||
import tempfile
|
import tempfile
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
|
# TODO support non-zip perf results?
|
||||||
|
|
||||||
|
|
||||||
PERF_PATHS = ['*.perf']
|
PERF_PATHS = ['*.perf']
|
||||||
PERF_TOOL = ['perf']
|
PERF_TOOL = ['perf']
|
||||||
@@ -118,61 +120,31 @@ class Int(co.namedtuple('Int', 'x')):
|
|||||||
# perf results
|
# perf results
|
||||||
class PerfResult(co.namedtuple('PerfResult', [
|
class PerfResult(co.namedtuple('PerfResult', [
|
||||||
'file', 'function', 'line',
|
'file', 'function', 'line',
|
||||||
'self_cycles',
|
'cycles', 'bmisses', 'branches', 'cmisses', 'caches',
|
||||||
'self_bmisses', 'self_branches',
|
'children'])):
|
||||||
'self_cmisses', 'self_caches',
|
|
||||||
'cycles',
|
|
||||||
'bmisses', 'branches',
|
|
||||||
'cmisses', 'caches',
|
|
||||||
'children', 'parents'])):
|
|
||||||
_by = ['file', 'function', 'line']
|
_by = ['file', 'function', 'line']
|
||||||
_fields = [
|
_fields = ['cycles', 'bmisses', 'branches', 'cmisses', 'caches']
|
||||||
'self_cycles',
|
|
||||||
'self_bmisses', 'self_branches',
|
|
||||||
'self_cmisses', 'self_caches',
|
|
||||||
'cycles',
|
|
||||||
'bmisses', 'branches',
|
|
||||||
'cmisses', 'caches']
|
|
||||||
_types = {
|
_types = {
|
||||||
'self_cycles': Int,
|
|
||||||
'self_bmisses': Int, 'self_branches': Int,
|
|
||||||
'self_cmisses': Int, 'self_caches': Int,
|
|
||||||
'cycles': Int,
|
'cycles': Int,
|
||||||
'bmisses': Int, 'branches': Int,
|
'bmisses': Int, 'branches': Int,
|
||||||
'cmisses': Int, 'caches': Int}
|
'cmisses': Int, 'caches': Int}
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
def __new__(cls, file='', function='', line=0,
|
def __new__(cls, file='', function='', line=0,
|
||||||
self_cycles=0,
|
cycles=0, bmisses=0, branches=0, cmisses=0, caches=0,
|
||||||
self_bmisses=0, self_branches=0,
|
children=[]):
|
||||||
self_cmisses=0, self_caches=0,
|
|
||||||
cycles=0,
|
|
||||||
bmisses=0, branches=0,
|
|
||||||
cmisses=0, caches=0,
|
|
||||||
children=set(), parents=set()):
|
|
||||||
return super().__new__(cls, file, function, int(Int(line)),
|
return super().__new__(cls, file, function, int(Int(line)),
|
||||||
Int(self_cycles),
|
Int(cycles), Int(bmisses), Int(branches), Int(cmisses), Int(caches),
|
||||||
Int(self_bmisses), Int(self_branches),
|
children)
|
||||||
Int(self_cmisses), Int(self_caches),
|
|
||||||
Int(cycles),
|
|
||||||
Int(bmisses), Int(branches),
|
|
||||||
Int(cmisses), Int(caches),
|
|
||||||
children, parents)
|
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return PerfResult(self.file, self.function, self.line,
|
return PerfResult(self.file, self.function, self.line,
|
||||||
self.self_cycles + other.self_cycles,
|
|
||||||
self.self_bmisses + other.self_bmisses,
|
|
||||||
self.self_branches + other.self_branches,
|
|
||||||
self.self_cmisses + other.self_cmisses,
|
|
||||||
self.self_caches + other.self_caches,
|
|
||||||
self.cycles + other.cycles,
|
self.cycles + other.cycles,
|
||||||
self.bmisses + other.bmisses,
|
self.bmisses + other.bmisses,
|
||||||
self.branches + other.branches,
|
self.branches + other.branches,
|
||||||
self.cmisses + other.cmisses,
|
self.cmisses + other.cmisses,
|
||||||
self.caches + other.caches,
|
self.caches + other.caches,
|
||||||
self.children | other.children,
|
self.children + other.children)
|
||||||
self.parents | other.parents)
|
|
||||||
|
|
||||||
|
|
||||||
def openio(path, mode='r'):
|
def openio(path, mode='r'):
|
||||||
@@ -245,7 +217,8 @@ def record(command, *,
|
|||||||
def collect_decompressed(path, *,
|
def collect_decompressed(path, *,
|
||||||
perf_tool=PERF_TOOL,
|
perf_tool=PERF_TOOL,
|
||||||
everything=False,
|
everything=False,
|
||||||
depth=0,
|
propagate=0,
|
||||||
|
depth=1,
|
||||||
**args):
|
**args):
|
||||||
sample_pattern = re.compile(
|
sample_pattern = re.compile(
|
||||||
'(?P<comm>\w+)'
|
'(?P<comm>\w+)'
|
||||||
@@ -278,10 +251,27 @@ def collect_decompressed(path, *,
|
|||||||
close_fds=False)
|
close_fds=False)
|
||||||
|
|
||||||
last_filtered = False
|
last_filtered = False
|
||||||
last_has_frame = False
|
|
||||||
last_event = ''
|
last_event = ''
|
||||||
last_period = 0
|
last_period = 0
|
||||||
results = co.defaultdict(lambda: co.defaultdict(lambda: (0, 0)))
|
last_stack = []
|
||||||
|
results = {}
|
||||||
|
|
||||||
|
def commit():
|
||||||
|
# tail-recursively propagate measurements
|
||||||
|
for i in range(len(last_stack)):
|
||||||
|
results_ = results
|
||||||
|
for j in reversed(range(i+1)):
|
||||||
|
if i+1-j > depth:
|
||||||
|
break
|
||||||
|
|
||||||
|
# propagate
|
||||||
|
name = last_stack[j]
|
||||||
|
if name not in results_:
|
||||||
|
results_[name] = (co.defaultdict(lambda: 0), {})
|
||||||
|
results_[name][0][last_event] += last_period
|
||||||
|
|
||||||
|
# recurse
|
||||||
|
results_ = results_[name][1]
|
||||||
|
|
||||||
for line in proc.stdout:
|
for line in proc.stdout:
|
||||||
# we need to process a lot of data, so wait to use regex as late
|
# we need to process a lot of data, so wait to use regex as late
|
||||||
@@ -291,10 +281,12 @@ def collect_decompressed(path, *,
|
|||||||
if not line.startswith('\t'):
|
if not line.startswith('\t'):
|
||||||
m = sample_pattern.match(line)
|
m = sample_pattern.match(line)
|
||||||
if m:
|
if m:
|
||||||
|
if last_stack:
|
||||||
|
commit()
|
||||||
last_event = m.group('event')
|
last_event = m.group('event')
|
||||||
last_filtered = last_event in events
|
last_filtered = last_event in events
|
||||||
last_period = int(m.group('period'), 0)
|
last_period = int(m.group('period'), 0)
|
||||||
last_has_frame = False
|
last_stack = []
|
||||||
elif last_filtered:
|
elif last_filtered:
|
||||||
m = frame_pattern.match(line)
|
m = frame_pattern.match(line)
|
||||||
if m:
|
if m:
|
||||||
@@ -305,20 +297,16 @@ def collect_decompressed(path, *,
|
|||||||
or not m.group('sym')[:1].isalpha()):
|
or not m.group('sym')[:1].isalpha()):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = (
|
last_stack.append((
|
||||||
m.group('dso'),
|
m.group('dso'),
|
||||||
m.group('sym'),
|
m.group('sym'),
|
||||||
int(m.group('addr'), 16))
|
int(m.group('addr'), 16)))
|
||||||
self, total = results[name][last_event]
|
|
||||||
if not last_has_frame:
|
# stop propogating?
|
||||||
results[name][last_event] = (
|
if propagate and len(last_stack) >= propagate:
|
||||||
self + last_period,
|
last_filtered = False
|
||||||
total + last_period)
|
if last_stack:
|
||||||
last_has_frame = True
|
commit()
|
||||||
else:
|
|
||||||
results[name][last_event] = (
|
|
||||||
self,
|
|
||||||
total + last_period)
|
|
||||||
|
|
||||||
proc.wait()
|
proc.wait()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
@@ -328,14 +316,15 @@ def collect_decompressed(path, *,
|
|||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# rearrange results into result type
|
# rearrange results into result type
|
||||||
results_ = []
|
def to_results(results):
|
||||||
for name, r in results.items():
|
results_ = []
|
||||||
results_.append(PerfResult(*name,
|
for name, (r, children) in results.items():
|
||||||
**{'self_'+events[e]: s for e, (s, _) in r.items()},
|
results_.append(PerfResult(*name,
|
||||||
**{ events[e]: t for e, (_, t) in r.items()}))
|
**{events[k]: v for k, v in r.items()},
|
||||||
results = results_
|
children=to_results(children)))
|
||||||
|
return results_
|
||||||
|
|
||||||
return results
|
return to_results(results)
|
||||||
|
|
||||||
def collect_job(path, i, **args):
|
def collect_job(path, i, **args):
|
||||||
# decompress into a temporary file, this is to work around
|
# decompress into a temporary file, this is to work around
|
||||||
@@ -567,38 +556,46 @@ def collect(paths, *,
|
|||||||
default=0)
|
default=0)
|
||||||
|
|
||||||
# then try to map addrs -> file+line
|
# then try to map addrs -> file+line
|
||||||
for r in results_:
|
#
|
||||||
addr = r.line + delta
|
# note we need to do this recursively
|
||||||
i = bisect.bisect(line_at, addr, key=lambda x: x[0])
|
def remap(results):
|
||||||
if i > 0:
|
results_ = []
|
||||||
_, file, line = line_at[i-1]
|
for r in results:
|
||||||
else:
|
addr = r.line + delta
|
||||||
file, line = re.sub('(\.o)?$', '.c', r.file, 1), 0
|
i = bisect.bisect(line_at, addr, key=lambda x: x[0])
|
||||||
|
if i > 0:
|
||||||
|
_, file, line = line_at[i-1]
|
||||||
|
else:
|
||||||
|
file, line = re.sub('(\.o)?$', '.c', r.file, 1), 0
|
||||||
|
|
||||||
# ignore filtered sources
|
# ignore filtered sources
|
||||||
if sources is not None:
|
if sources is not None:
|
||||||
if not any(
|
if not any(
|
||||||
os.path.abspath(file) == os.path.abspath(s)
|
os.path.abspath(file) == os.path.abspath(s)
|
||||||
for s in sources):
|
for s in sources):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# default to only cwd
|
# default to only cwd
|
||||||
if not everything and not os.path.commonpath([
|
if not everything and not os.path.commonpath([
|
||||||
|
os.getcwd(),
|
||||||
|
os.path.abspath(file)]) == os.getcwd():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# simplify path
|
||||||
|
if os.path.commonpath([
|
||||||
os.getcwd(),
|
os.getcwd(),
|
||||||
os.path.abspath(file)]) == os.getcwd():
|
os.path.abspath(file)]) == os.getcwd():
|
||||||
continue
|
file = os.path.relpath(file)
|
||||||
|
else:
|
||||||
|
file = os.path.abspath(file)
|
||||||
|
|
||||||
# simplify path
|
function, *_ = r.function.split('+', 1)
|
||||||
if os.path.commonpath([
|
results_.append(r._replace(
|
||||||
os.getcwd(),
|
file=file, function=function, line=line,
|
||||||
os.path.abspath(file)]) == os.getcwd():
|
children=remap(r.children)))
|
||||||
file = os.path.relpath(file)
|
return results_
|
||||||
else:
|
|
||||||
file = os.path.abspath(file)
|
|
||||||
|
|
||||||
function, *_ = r.function.split('+', 1)
|
results.extend(remap(results_))
|
||||||
results.append(PerfResult(file, function, line,
|
|
||||||
**{k: getattr(r, k) for k in PerfResult._fields}))
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -636,6 +633,15 @@ def fold(Result, results, *,
|
|||||||
for name, rs in folding.items():
|
for name, rs in folding.items():
|
||||||
folded.append(sum(rs[1:], start=rs[0]))
|
folded.append(sum(rs[1:], start=rs[0]))
|
||||||
|
|
||||||
|
# fold recursively
|
||||||
|
folded_ = []
|
||||||
|
for r in folded:
|
||||||
|
folded_.append(r._replace(children=fold(
|
||||||
|
Result, r.children,
|
||||||
|
by=by,
|
||||||
|
defines=defines)))
|
||||||
|
folded = folded_
|
||||||
|
|
||||||
return folded
|
return folded
|
||||||
|
|
||||||
def table(Result, results, diff_results=None, *,
|
def table(Result, results, diff_results=None, *,
|
||||||
@@ -645,6 +651,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
summary=False,
|
summary=False,
|
||||||
all=False,
|
all=False,
|
||||||
percent=False,
|
percent=False,
|
||||||
|
depth=1,
|
||||||
**_):
|
**_):
|
||||||
all_, all = all, __builtins__.all
|
all_, all = all, __builtins__.all
|
||||||
|
|
||||||
@@ -683,13 +690,12 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if getattr(table.get(n), k, None) is not None else (),
|
if getattr(table.get(n), k, None) is not None else (),
|
||||||
reverse=reverse ^ (not k or k in Result._fields))
|
reverse=reverse ^ (not k or k in Result._fields))
|
||||||
|
|
||||||
|
|
||||||
# build up our lines
|
# build up our lines
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -698,129 +704,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
@@ -830,13 +802,83 @@ def table(Result, results, diff_results=None, *,
|
|||||||
it.chain([23], it.repeat(7)),
|
it.chain([23], it.repeat(7)),
|
||||||
range(len(lines[0])-1))]
|
range(len(lines[0])-1))]
|
||||||
|
|
||||||
# print our table
|
# adjust the name width based on the expected call depth, though
|
||||||
for line in lines:
|
# note this doesn't really work with unbounded recursion
|
||||||
print('%-*s %s%s' % (
|
if not summary and not m.isinf(depth):
|
||||||
widths[0], line[0],
|
widths[0] += 4*(depth-1)
|
||||||
' '.join('%*s' % (w, x)
|
|
||||||
for w, x in zip(widths[1:], line[1:-1])),
|
# print the tree recursively
|
||||||
line[-1]))
|
print('%-*s %s%s' % (
|
||||||
|
widths[0], lines[0][0],
|
||||||
|
' '.join('%*s' % (w, x)
|
||||||
|
for w, x in zip(widths[1:], lines[0][1:-1])),
|
||||||
|
lines[0][-1]))
|
||||||
|
|
||||||
|
if not summary:
|
||||||
|
def recurse(results_, depth_, prefixes=('', '', '', '')):
|
||||||
|
# rebuild our tables at each layer
|
||||||
|
table_ = {
|
||||||
|
','.join(str(getattr(r, k) or '') for k in by): r
|
||||||
|
for r in results_}
|
||||||
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
|
# sort again at each layer, keep in mind the numbers are
|
||||||
|
# changing as we descend
|
||||||
|
names_.sort()
|
||||||
|
if sort:
|
||||||
|
for k, reverse in reversed(sort):
|
||||||
|
names_.sort(key=lambda n: (getattr(table_[n], k),)
|
||||||
|
if getattr(table_.get(n), k, None) is not None else (),
|
||||||
|
reverse=reverse ^ (not k or k in Result._fields))
|
||||||
|
|
||||||
|
for i, name in enumerate(names_):
|
||||||
|
r = table_[name]
|
||||||
|
is_last = (i == len(names_)-1)
|
||||||
|
|
||||||
|
print('%s%-*s %s' % (
|
||||||
|
prefixes[0+is_last],
|
||||||
|
widths[0] - (
|
||||||
|
len(prefixes[0+is_last])
|
||||||
|
if not m.isinf(depth) else 0),
|
||||||
|
name,
|
||||||
|
' '.join('%*s' % (w, x)
|
||||||
|
for w, x in zip(
|
||||||
|
widths[1:],
|
||||||
|
table_entry(name, r)[1:]))))
|
||||||
|
|
||||||
|
# recurse?
|
||||||
|
if depth_ > 1:
|
||||||
|
recurse(
|
||||||
|
r.children,
|
||||||
|
depth_-1,
|
||||||
|
(prefixes[2+is_last] + "|-> ",
|
||||||
|
prefixes[2+is_last] + "'-> ",
|
||||||
|
prefixes[2+is_last] + "| ",
|
||||||
|
prefixes[2+is_last] + " "))
|
||||||
|
|
||||||
|
# we have enough going on with diffing to make the top layer
|
||||||
|
# a special case
|
||||||
|
for name, line in zip(names, lines[1:-1]):
|
||||||
|
print('%-*s %s%s' % (
|
||||||
|
widths[0], line[0],
|
||||||
|
' '.join('%*s' % (w, x)
|
||||||
|
for w, x in zip(widths[1:], line[1:-1])),
|
||||||
|
line[-1]))
|
||||||
|
|
||||||
|
if name in table and depth > 1:
|
||||||
|
recurse(
|
||||||
|
table[name].children,
|
||||||
|
depth-1,
|
||||||
|
("|-> ",
|
||||||
|
"'-> ",
|
||||||
|
"| ",
|
||||||
|
" "))
|
||||||
|
|
||||||
|
print('%-*s %s%s' % (
|
||||||
|
widths[0], lines[-1][0],
|
||||||
|
' '.join('%*s' % (w, x)
|
||||||
|
for w, x in zip(widths[1:], lines[-1][1:-1])),
|
||||||
|
lines[-1][-1]))
|
||||||
|
|
||||||
|
|
||||||
def annotate(Result, results, *,
|
def annotate(Result, results, *,
|
||||||
@@ -855,11 +897,11 @@ def annotate(Result, results, *,
|
|||||||
t0, t1 = min(t0, t1), max(t0, t1)
|
t0, t1 = min(t0, t1), max(t0, t1)
|
||||||
|
|
||||||
if not branches and not caches:
|
if not branches and not caches:
|
||||||
tk = 'self_cycles'
|
tk = 'cycles'
|
||||||
elif branches:
|
elif branches:
|
||||||
tk = 'self_bmisses'
|
tk = 'bmisses'
|
||||||
else:
|
else:
|
||||||
tk = 'self_cmisses'
|
tk = 'cmisses'
|
||||||
|
|
||||||
# find max cycles
|
# find max cycles
|
||||||
max_ = max(it.chain((float(getattr(r, tk)) for r in results), [1]))
|
max_ = max(it.chain((float(getattr(r, tk)) for r in results), [1]))
|
||||||
@@ -913,23 +955,19 @@ def annotate(Result, results, *,
|
|||||||
|
|
||||||
r = table.get(i+1)
|
r = table.get(i+1)
|
||||||
if r is not None and (
|
if r is not None and (
|
||||||
float(r.self_cycles) > 0
|
float(r.cycles) > 0
|
||||||
if not branches and not caches
|
if not branches and not caches
|
||||||
else float(r.self_bmisses) > 0
|
else float(r.bmisses) > 0 or float(r.branches) > 0
|
||||||
or float(r.self_branches) > 0
|
|
||||||
if branches
|
if branches
|
||||||
else float(r.self_cmisses) > 0
|
else float(r.cmisses) > 0 or float(r.caches) > 0):
|
||||||
or float(r.self_caches) > 0):
|
|
||||||
line = '%-*s // %s' % (
|
line = '%-*s // %s' % (
|
||||||
args['width'],
|
args['width'],
|
||||||
line,
|
line,
|
||||||
'%s cycles' % r.self_cycles
|
'%s cycles' % r.cycles
|
||||||
if not branches and not caches
|
if not branches and not caches
|
||||||
else '%s bmisses, %s branches' % (
|
else '%s bmisses, %s branches' % (r.bmisses, r.branches)
|
||||||
r.self_bmisses, r.self_branches)
|
|
||||||
if branches
|
if branches
|
||||||
else '%s cmisses, %s caches' % (
|
else '%s cmisses, %s caches' % (r.cmisses, r.caches))
|
||||||
r.self_cmisses, r.self_caches))
|
|
||||||
|
|
||||||
if args['color']:
|
if args['color']:
|
||||||
if float(getattr(r, tk)) / max_ >= t1:
|
if float(getattr(r, tk)) / max_ >= t1:
|
||||||
@@ -948,8 +986,6 @@ def report(perf_paths, *,
|
|||||||
self=False,
|
self=False,
|
||||||
branches=False,
|
branches=False,
|
||||||
caches=False,
|
caches=False,
|
||||||
tree=False,
|
|
||||||
depth=None,
|
|
||||||
**args):
|
**args):
|
||||||
# figure out what color should be
|
# figure out what color should be
|
||||||
if args.get('color') == 'auto':
|
if args.get('color') == 'auto':
|
||||||
@@ -959,11 +995,8 @@ def report(perf_paths, *,
|
|||||||
else:
|
else:
|
||||||
args['color'] = False
|
args['color'] = False
|
||||||
|
|
||||||
# it doesn't really make sense to not have a depth with tree,
|
# depth of 0 == m.inf
|
||||||
# so assume depth=inf if tree by default
|
if args.get('depth') == 0:
|
||||||
if args.get('depth') is None:
|
|
||||||
args['depth'] = m.inf if tree else 1
|
|
||||||
elif args.get('depth') == 0:
|
|
||||||
args['depth'] = m.inf
|
args['depth'] = m.inf
|
||||||
|
|
||||||
# find sizes
|
# find sizes
|
||||||
@@ -1055,12 +1088,10 @@ def report(perf_paths, *,
|
|||||||
table(PerfResult, results,
|
table(PerfResult, results,
|
||||||
diff_results if args.get('diff') else None,
|
diff_results if args.get('diff') else None,
|
||||||
by=by if by is not None else ['function'],
|
by=by if by is not None else ['function'],
|
||||||
fields=fields if fields is not None else [
|
fields=fields if fields is not None
|
||||||
'self_'+k if self else k
|
else ['cycles'] if not branches and not caches
|
||||||
for k in (
|
else ['bmisses', 'branches'] if branches
|
||||||
['cycles'] if not branches and not caches
|
else ['cmisses', 'caches'],
|
||||||
else ['bmisses', 'branches'] if branches
|
|
||||||
else ['cmisses', 'caches'])],
|
|
||||||
sort=sort,
|
sort=sort,
|
||||||
**args)
|
**args)
|
||||||
|
|
||||||
@@ -1164,10 +1195,6 @@ if __name__ == "__main__":
|
|||||||
'--everything',
|
'--everything',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="Include builtin and libc specific symbols.")
|
help="Include builtin and libc specific symbols.")
|
||||||
parser.add_argument(
|
|
||||||
'--self',
|
|
||||||
action='store_true',
|
|
||||||
help="Show samples before propagation up the call-chain.")
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--branches',
|
'--branches',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -1176,6 +1203,18 @@ if __name__ == "__main__":
|
|||||||
'--caches',
|
'--caches',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="Show cache accesses and cache misses.")
|
help="Show cache accesses and cache misses.")
|
||||||
|
parser.add_argument(
|
||||||
|
'-P', '--propagate',
|
||||||
|
type=lambda x: int(x, 0),
|
||||||
|
help="Depth to propagate samples up the call-stack. 0 propagates up "
|
||||||
|
"to the entry point, 1 does no propagation. Defaults to 0.")
|
||||||
|
parser.add_argument(
|
||||||
|
'-Z', '--depth',
|
||||||
|
nargs='?',
|
||||||
|
type=lambda x: int(x, 0),
|
||||||
|
const=0,
|
||||||
|
help="Depth of function calls to show. 0 shows all calls but may not "
|
||||||
|
"terminate!")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-A', '--annotate',
|
'-A', '--annotate',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
|||||||
+86
-125
@@ -102,23 +102,23 @@ class Int(co.namedtuple('Int', 'x')):
|
|||||||
|
|
||||||
# size results
|
# size results
|
||||||
class StackResult(co.namedtuple('StackResult', [
|
class StackResult(co.namedtuple('StackResult', [
|
||||||
'file', 'function', 'frame', 'limit', 'calls'])):
|
'file', 'function', 'frame', 'limit', 'children'])):
|
||||||
_by = ['file', 'function']
|
_by = ['file', 'function']
|
||||||
_fields = ['frame', 'limit']
|
_fields = ['frame', 'limit']
|
||||||
_types = {'frame': Int, 'limit': Int}
|
_types = {'frame': Int, 'limit': Int}
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
def __new__(cls, file='', function='',
|
def __new__(cls, file='', function='',
|
||||||
frame=0, limit=0, calls=set()):
|
frame=0, limit=0, children=set()):
|
||||||
return super().__new__(cls, file, function,
|
return super().__new__(cls, file, function,
|
||||||
Int(frame), Int(limit),
|
Int(frame), Int(limit),
|
||||||
calls)
|
children)
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return StackResult(self.file, self.function,
|
return StackResult(self.file, self.function,
|
||||||
self.frame + other.frame,
|
self.frame + other.frame,
|
||||||
max(self.limit, other.limit),
|
max(self.limit, other.limit),
|
||||||
self.calls | other.calls)
|
self.children | other.children)
|
||||||
|
|
||||||
|
|
||||||
def openio(path, mode='r'):
|
def openio(path, mode='r'):
|
||||||
@@ -256,20 +256,20 @@ def collect(paths, *,
|
|||||||
|
|
||||||
return frame + limit
|
return frame + limit
|
||||||
|
|
||||||
def find_calls(targets):
|
def find_children(targets):
|
||||||
calls = set()
|
children = set()
|
||||||
for target in targets:
|
for target in targets:
|
||||||
if target in callgraph:
|
if target in callgraph:
|
||||||
t_file, t_function, _, _ = callgraph[target]
|
t_file, t_function, _, _ = callgraph[target]
|
||||||
calls.add((t_file, t_function))
|
children.add((t_file, t_function))
|
||||||
return calls
|
return children
|
||||||
|
|
||||||
# build results
|
# build results
|
||||||
results = []
|
results = []
|
||||||
for source, (s_file, s_function, frame, targets) in callgraph.items():
|
for source, (s_file, s_function, frame, targets) in callgraph.items():
|
||||||
limit = find_limit(source)
|
limit = find_limit(source)
|
||||||
calls = find_calls(targets)
|
children = find_children(targets)
|
||||||
results.append(StackResult(s_file, s_function, frame, limit, calls))
|
results.append(StackResult(s_file, s_function, frame, limit, children))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -361,8 +361,8 @@ def table(Result, results, diff_results=None, *,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -371,129 +371,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
@@ -505,22 +471,17 @@ def table(Result, results, diff_results=None, *,
|
|||||||
|
|
||||||
# adjust the name width based on the expected call depth, though
|
# adjust the name width based on the expected call depth, though
|
||||||
# note this doesn't really work with unbounded recursion
|
# note this doesn't really work with unbounded recursion
|
||||||
if not summary:
|
if not summary and not m.isinf(depth):
|
||||||
if not m.isinf(depth):
|
widths[0] += 4*(depth-1)
|
||||||
widths[0] += 4*(depth-1)
|
|
||||||
|
|
||||||
# print our table with optional call info
|
# print the tree recursively
|
||||||
#
|
|
||||||
# note we try to adjust our name width based on expected call depth, but
|
|
||||||
# this doesn't work if there's unbounded recursion
|
|
||||||
if not tree:
|
if not tree:
|
||||||
print('%-*s %s%s' % (
|
print('%-*s %s%s' % (
|
||||||
widths[0], lines[0][0],
|
widths[0], lines[0][0],
|
||||||
' '.join('%*s' % (w, x)
|
' '.join('%*s' % (w, x)
|
||||||
for w, x, in zip(widths[1:], lines[0][1:-1])),
|
for w, x in zip(widths[1:], lines[0][1:-1])),
|
||||||
lines[0][-1]))
|
lines[0][-1]))
|
||||||
|
|
||||||
# print the tree recursively
|
|
||||||
if not summary:
|
if not summary:
|
||||||
line_table = {n: l for n, l in zip(names, lines[1:-1])}
|
line_table = {n: l for n, l in zip(names, lines[1:-1])}
|
||||||
|
|
||||||
@@ -541,32 +502,32 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not tree:
|
if not tree:
|
||||||
print(' %s%s' % (
|
print(' %s%s' % (
|
||||||
' '.join('%*s' % (w, x)
|
' '.join('%*s' % (w, x)
|
||||||
for w, x, in zip(widths[1:], line[1:-1])),
|
for w, x in zip(widths[1:], line[1:-1])),
|
||||||
line[-1]),
|
line[-1]),
|
||||||
end='')
|
end='')
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# recurse?
|
# recurse?
|
||||||
if name in table and depth_ > 0:
|
if name in table and depth_ > 1:
|
||||||
calls = {
|
children = {
|
||||||
','.join(str(getattr(Result(*c), k) or '') for k in by)
|
','.join(str(getattr(Result(*c), k) or '') for k in by)
|
||||||
for c in table[name].calls}
|
for c in table[name].children}
|
||||||
|
|
||||||
recurse(
|
recurse(
|
||||||
# note we're maintaining sort order
|
# note we're maintaining sort order
|
||||||
[n for n in names if n in calls],
|
[n for n in names if n in children],
|
||||||
depth_-1,
|
depth_-1,
|
||||||
(prefixes[2+is_last] + "|-> ",
|
(prefixes[2+is_last] + "|-> ",
|
||||||
prefixes[2+is_last] + "'-> ",
|
prefixes[2+is_last] + "'-> ",
|
||||||
prefixes[2+is_last] + "| ",
|
prefixes[2+is_last] + "| ",
|
||||||
prefixes[2+is_last] + " "))
|
prefixes[2+is_last] + " "))
|
||||||
recurse(names, depth-1)
|
|
||||||
|
recurse(names, depth)
|
||||||
|
|
||||||
if not tree:
|
if not tree:
|
||||||
print('%-*s %s%s' % (
|
print('%-*s %s%s' % (
|
||||||
widths[0], lines[-1][0],
|
widths[0], lines[-1][0],
|
||||||
' '.join('%*s' % (w, x)
|
' '.join('%*s' % (w, x)
|
||||||
for w, x, in zip(widths[1:], lines[-1][1:-1])),
|
for w, x in zip(widths[1:], lines[-1][1:-1])),
|
||||||
lines[-1][-1]))
|
lines[-1][-1]))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+68
-99
@@ -217,6 +217,9 @@ def collect(obj_paths, *,
|
|||||||
s_file = int(m.group('file'))
|
s_file = int(m.group('file'))
|
||||||
elif m.group('size'):
|
elif m.group('size'):
|
||||||
s_size = int(m.group('size'))
|
s_size = int(m.group('size'))
|
||||||
|
if is_struct:
|
||||||
|
file = files.get(s_file, '?')
|
||||||
|
results_.append(StructResult(file, s_name, s_size))
|
||||||
proc.wait()
|
proc.wait()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
if not args.get('verbose'):
|
if not args.get('verbose'):
|
||||||
@@ -250,7 +253,7 @@ def collect(obj_paths, *,
|
|||||||
else:
|
else:
|
||||||
file = os.path.abspath(r.file)
|
file = os.path.abspath(r.file)
|
||||||
|
|
||||||
results.append(StructResult(r.file, r.struct, r.size))
|
results.append(r._replace(file=file))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -340,8 +343,8 @@ def table(Result, results, diff_results=None, *,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -350,129 +353,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
|
|||||||
+64
-98
@@ -431,8 +431,8 @@ def table(Result, results, diff_results=None, *,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# header
|
# header
|
||||||
line = []
|
header = []
|
||||||
line.append('%s%s' % (
|
header.append('%s%s' % (
|
||||||
','.join(by),
|
','.join(by),
|
||||||
' (%d added, %d removed)' % (
|
' (%d added, %d removed)' % (
|
||||||
sum(1 for n in table if n not in diff_table),
|
sum(1 for n in table if n not in diff_table),
|
||||||
@@ -441,129 +441,95 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if not summary else '')
|
if not summary else '')
|
||||||
if diff_results is None:
|
if diff_results is None:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
elif percent:
|
elif percent:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append(k)
|
header.append(k)
|
||||||
else:
|
else:
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('o'+k)
|
header.append('o'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('n'+k)
|
header.append('n'+k)
|
||||||
for k in fields:
|
for k in fields:
|
||||||
line.append('d'+k)
|
header.append('d'+k)
|
||||||
line.append('')
|
header.append('')
|
||||||
lines.append(line)
|
lines.append(header)
|
||||||
|
|
||||||
|
def table_entry(name, r, diff_r=None, ratios=[]):
|
||||||
|
entry = []
|
||||||
|
entry.append(name)
|
||||||
|
if diff_results is None:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].none)
|
||||||
|
elif percent:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
else:
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(diff_r, k).diff_table()
|
||||||
|
if getattr(diff_r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(getattr(r, k).diff_table()
|
||||||
|
if getattr(r, k, None) is not None
|
||||||
|
else types[k].diff_none)
|
||||||
|
for k in fields:
|
||||||
|
entry.append(types[k].diff_diff(
|
||||||
|
getattr(r, k, None),
|
||||||
|
getattr(diff_r, k, None)))
|
||||||
|
if diff_results is None:
|
||||||
|
entry.append('')
|
||||||
|
elif percent:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios))
|
||||||
|
else:
|
||||||
|
entry.append(' (%s)' % ', '.join(
|
||||||
|
'+∞%' if t == +m.inf
|
||||||
|
else '-∞%' if t == -m.inf
|
||||||
|
else '%+.1f%%' % (100*t)
|
||||||
|
for t in ratios
|
||||||
|
if t)
|
||||||
|
if any(ratios) else '')
|
||||||
|
return entry
|
||||||
|
|
||||||
# entries
|
# entries
|
||||||
if not summary:
|
if not summary:
|
||||||
for name in names:
|
for name in names:
|
||||||
r = table.get(name)
|
r = table.get(name)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = diff_table.get(name)
|
diff_r = diff_table.get(name)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
if not any(ratios) and not all_:
|
if not all_ and not any(ratios):
|
||||||
continue
|
continue
|
||||||
|
lines.append(table_entry(name, r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append(name)
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# total
|
# total
|
||||||
r = next(iter(fold(Result, results, by=[])), None)
|
r = next(iter(fold(Result, results, by=[])), None)
|
||||||
if diff_results is not None:
|
if diff_results is None:
|
||||||
|
diff_r = None
|
||||||
|
ratios = None
|
||||||
|
else:
|
||||||
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
diff_r = next(iter(fold(Result, diff_results, by=[])), None)
|
||||||
ratios = [
|
ratios = [
|
||||||
types[k].ratio(
|
types[k].ratio(
|
||||||
getattr(r, k, None),
|
getattr(r, k, None),
|
||||||
getattr(diff_r, k, None))
|
getattr(diff_r, k, None))
|
||||||
for k in fields]
|
for k in fields]
|
||||||
|
lines.append(table_entry('TOTAL', r, diff_r, ratios))
|
||||||
line = []
|
|
||||||
line.append('TOTAL')
|
|
||||||
if diff_results is None:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].none)
|
|
||||||
elif percent:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
else:
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(diff_r, k).diff_table()
|
|
||||||
if getattr(diff_r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(getattr(r, k).diff_table()
|
|
||||||
if getattr(r, k, None) is not None
|
|
||||||
else types[k].diff_none)
|
|
||||||
for k in fields:
|
|
||||||
line.append(types[k].diff_diff(
|
|
||||||
getattr(r, k, None),
|
|
||||||
getattr(diff_r, k, None)))
|
|
||||||
if diff_results is None:
|
|
||||||
line.append('')
|
|
||||||
elif percent:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios))
|
|
||||||
else:
|
|
||||||
line.append(' (%s)' % ', '.join(
|
|
||||||
'+∞%' if t == +m.inf
|
|
||||||
else '-∞%' if t == -m.inf
|
|
||||||
else '%+.1f%%' % (100*t)
|
|
||||||
for t in ratios
|
|
||||||
if t)
|
|
||||||
if any(ratios) else '')
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
# find the best widths, note that column 0 contains the names and column -1
|
# find the best widths, note that column 0 contains the names and column -1
|
||||||
# the ratios, so those are handled a bit differently
|
# the ratios, so those are handled a bit differently
|
||||||
|
|||||||
Reference in New Issue
Block a user