forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiler_inputs_size_diff.py
executable file
·173 lines (142 loc) · 5.33 KB
/
compiler_inputs_size_diff.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/usr/bin/env python3
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Calculate deltas between results of compiler_inputs_size.py.
As input, the script takes the outputs of compiler_inputs_size.py produced from
two different builds. The output is a list of translation units and deltas in
bytes sorted by deltas in decreasing order. TUs that didn't change in size are
omitted.
Example usage:
$ tools/clang/scripts/compiler_inputs_size_diff.py before.txt after.txt
Before: 343.41 GiB (368729376427)
After: 345.43 GiB (370904487700)
Delta: +2.03 GiB (+2175111273)
Delta %: 0.59%
[...]
third_party/blink/renderer/core/dom/node.cc +601503
third_party/blink/renderer/core/frame/frame.cc +560405
third_party/blink/renderer/core/fetch/body.cc -75
third_party/blink/renderer/core/url/dom_url.cc -75
third_party/blink/renderer/modules/ml/ml.cc -75
chrome/browser/ui/browser.cc -287
content/browser/browser_interface_binders.cc -287
[...]
Test this code with:
$ python3 -m doctest -v tools/clang/scripts/compiler_inputs_size_diff.py
"""
import argparse
import re
import sys
from typing import Dict, Iterable
LINE_RE = re.compile(r'(.+) (\d+)\n')
def parse_report_into_tu_size_dict(lines: Iterable[str]) -> Dict[str, int]:
r"""Parse a report from compiler_inputs_size.py into a dictionary.
The report is expected to have one line per translation unit followed by
a blank line and then a line with the total size.
Args:
lines: An iterable of lines from the report.
Returns:
A dictionary mapping translation unit paths (including a "total" key) to
their sizes in bytes.
>>> parse_report_into_tu_size_dict(
... 'foo.cc 1234\n'
... 'bar.cc 5678\n'
... '\n'
... 'Total: 6912\n'.splitlines(keepends=True))
{'foo.cc': 1234, 'bar.cc': 5678, 'total': 6912}
"""
sizes = {}
lines_iter = iter(lines)
for line in lines_iter:
m = LINE_RE.match(line)
if not m:
assert (line == '\n')
line = next(lines_iter)
sizes['total'] = int(line.rstrip().split(' ')[1])
break
sizes[m.group(1)] = int(m.group(2))
return sizes
def diff_tu_sizes(d1: Dict[str, int], d2: Dict[str, int]) -> Dict[str, int]:
r"""Calculate the size diff for each translation unit between two reports.
Args:
d1: dict mapping translation unit paths to their sizes from the
first report.
d2: dict mapping translation unit paths to their sizes from the
second report.
Returns:
A dictionary mapping translation unit paths to their size differences.
Includes entries for all TUs present in either report.
>>> diff_tu_sizes(
... {'foo.cc': 1234, 'bar.cc': 5678},
... {'foo.cc': 1200, 'baz.cc': 9012})
{'foo.cc': -34, 'bar.cc': -5678, 'baz.cc': 9012}
"""
size_diffs = {}
for path, size in d1.items():
size_diffs[path] = d2.get(path, 0) - size
remaining_keys = set(d2) - set(d1)
for path in remaining_keys:
size_diffs[path] = d2[path]
return size_diffs
def bytes_to_human(bytes: int, sign: bool = False) -> str:
"""Converts a number of bytes to a human-readable string.
Args:
bytes: The number of bytes to convert.
sign: whether to prefix with the sign if positive.
Returns:
A human-readable string representation of the number of bytes.
"""
units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']
i = 0
while bytes >= 1024 and i < len(units) - 1:
bytes /= 1024
i += 1
if sign:
return f'{bytes:+.2f} {units[i]}'
return f'{bytes:.2f} {units[i]}'
def print_diff(before: Dict[str, int], after: Dict[str, int]):
r"""Print the diff between two compiler_inputs_size.py reports.
>>> print_diff(
... {'foo.cc': 1234, 'bar.cc': 5678, 'total': 6912},
... {'foo.cc': 1200, 'bar.cc': 5678, 'baz.cc': 1012, 'total': 7912})
Before: 6.75 KiB (6912)
After: 7.73 KiB (7912)
Delta: +1000.00 B (+1000)
Delta %: 14.47%
baz.cc +1012
foo.cc -34
"""
size_diffs = diff_tu_sizes(before, after)
max_path_length = max(len(k) for k in size_diffs)
if max_path_length > 100:
max_path_length = 100
size_diffs = sorted([(k, v) for (k, v) in size_diffs.items() if v != 0],
key=lambda x: -x[1])
before_total = before['total']
after_total = after['total']
delta = after_total - before_total
print(f'Before: {bytes_to_human(before_total)} ({before_total})')
print(f'After: {bytes_to_human(after_total)} ({after_total})')
print(f'Delta: {bytes_to_human(delta, sign=True)} ({(delta):+d})')
print(f'Delta %: {(delta) / before_total * 100:.2f}%')
for name, size in size_diffs:
if name == 'total':
continue
print('{} {:+d}'.format(name.ljust(max_path_length), size))
def main():
parser = argparse.ArgumentParser(
description='Calculate deltas between results of compiler_inputs_size.py')
parser.add_argument('before',
type=argparse.FileType('r'),
help='First report from compiler_inputs_size.py.')
parser.add_argument('after',
type=argparse.FileType('r'),
help='Second report from compiler_inputs_size.py.')
args = parser.parse_args()
before = parse_report_into_tu_size_dict(args.before)
after = parse_report_into_tu_size_dict(args.after)
print_diff(before, after)
return 0
if __name__ == '__main__':
sys.exit(main())