-
Notifications
You must be signed in to change notification settings - Fork 492
/
Copy pathfile_size_compare.py
177 lines (143 loc) · 5.14 KB
/
file_size_compare.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
174
175
176
177
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
"""
Compare binary file sizes. Used by the Skycastle workflow to ensure no change adds excessive size to executorch.
Usage: file_size_compare.py [-h] --compare-file FILE [--base-file FILE] [-s, --max-size SIZE] [-e, --error-size SIZE] [-w, --warning-size SIZE]
Exit Codes:
0 - OK
1 - Comparison yielded a warning
2 - Comparison yielded an error
3 - Script errored while executing
"""
import argparse
import os
import sys
from pathlib import Path
# Exit codes.
EXIT_OK = 0
EXIT_WARNING = 1
EXIT_ERROR = 2
EXIT_SCRIPT_ERROR = 3
# TTY ANSI color codes.
TTY_GREEN = "\033[0;32m"
TTY_RED = "\033[0;31m"
TTY_RESET = "\033[0m"
# Error message printed if size is exceeded.
SIZE_ERROR_MESSAGE = """This diff is increasing the binary size of ExecuTorch (the PyTorch Edge model executor) by a large amount.
ExecuTorch has strict size requirements due to its embedded use case. Please follow these steps:
1. Check the output of the two steps (Build ... with the base commit/diff version) and compare their executable section sizes.
2. Contact a member of #pytorch_edge_portability so we can better help you.
"""
def create_file_path(file_name: str) -> Path:
"""Create Path object from file name string."""
file_path = Path(file_name)
if not file_path.is_file():
print(f"{file_path} is not a valid file path")
sys.exit(EXIT_SCRIPT_ERROR)
return file_path
def get_file_size(file_path: Path) -> int:
"""Get the size of a file on disk."""
return os.path.getsize(file_path)
def print_ansi(ansi_code: str) -> None:
"""Print an ANSI escape code."""
if sys.stdout.isatty():
print(ansi_code, end="")
def print_size_diff(compare_file: str, base_file: str, delta: int) -> None:
"""Print the size difference."""
if delta > 0:
print(f"{compare_file} is {delta} bytes bigger than {base_file}.")
else:
print_ansi(TTY_GREEN)
print(f"{compare_file} is {abs(delta)} bytes SMALLER than {base_file}. Great!")
print_ansi(TTY_RESET)
def print_size_error() -> None:
"""Print an error message for excessive size."""
print_ansi(TTY_RED)
print(SIZE_ERROR_MESSAGE)
print_ansi(TTY_RESET)
def compare_against_base(
base_file: str, compare_file: str, warning_size: int, error_size: int
) -> int:
"""Compare test binary file size against base revision binary file size."""
base_file = create_file_path(base_file)
compare_file = create_file_path(compare_file)
diff = get_file_size(compare_file) - get_file_size(base_file)
print_size_diff(compare_file.name, base_file.name, diff)
if diff >= error_size:
print_size_error()
return EXIT_ERROR
elif diff >= warning_size:
return EXIT_WARNING
else:
return EXIT_OK
def compare_against_max(compare_file: str, max_size: int) -> int:
"""Compare test binary file size against maximum value."""
compare_file = create_file_path(compare_file)
diff = get_file_size(compare_file) - max_size
print_size_diff(compare_file.name, "specified max size", diff)
if diff > 0:
print_size_error()
return EXIT_ERROR
else:
return EXIT_OK
def main() -> int:
# Parse arguments.
parser = argparse.ArgumentParser(description="Compare binary file size")
parser.add_argument(
"--compare-file",
metavar="FILE",
type=str,
required=True,
help="Binary to compare against size args or base revision binary",
)
parser.add_argument(
"--base-file",
metavar="FILE",
type=str,
help="Base revision binary",
dest="base_file",
)
parser.add_argument(
"-s, --max-size",
metavar="SIZE",
type=int,
help="Max size of the binary, in bytes",
dest="max_size",
)
parser.add_argument(
"-e, --error-size",
metavar="SIZE",
type=int,
help="Size difference between binaries constituting an error, in bytes",
dest="error_size",
)
parser.add_argument(
"-w, --warning-size",
metavar="SIZE",
type=int,
help="Size difference between binaries constituting a warning, in bytes",
dest="warning_size",
)
args = parser.parse_args()
if args.base_file is not None:
if args.max_size is not None:
print("Cannot specify both base file and maximum size arguments.")
sys.exit(EXIT_SCRIPT_ERROR)
if args.error_size is None or args.warning_size is None:
print(
"When comparing against base revision, error and warning sizes must be specified."
)
sys.exit(EXIT_SCRIPT_ERROR)
return compare_against_base(
args.base_file, args.compare_file, args.warning_size, args.error_size
)
elif args.max_size is not None:
return compare_against_max(args.compare_file, args.max_size)
return 0
if __name__ == "__main__":
sys.exit(main())