binary_size.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 #
3 # Copyright 2018 gRPC authors.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 import argparse
18 import glob
19 import multiprocessing
20 import os
21 import shutil
22 import subprocess
23 import sys
24 
25 from parse_link_map import parse_link_map
26 
27 sys.path.append(
28  os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'run_tests',
29  'python_utils'))
30 import check_on_pr
31 
32 # Only show diff 1KB or greater
33 _DIFF_THRESHOLD = 1000
34 
35 _SIZE_LABELS = ('Core', 'ObjC', 'BoringSSL', 'Protobuf', 'Total')
36 
37 argp = argparse.ArgumentParser(
38  description='Binary size diff of gRPC Objective-C sample')
39 
40 argp.add_argument('-d',
41  '--diff_base',
42  type=str,
43  help='Commit or branch to compare the current one to')
44 
45 args = argp.parse_args()
46 
47 
48 def dir_size(dir):
49  total = 0
50  for dirpath, dirnames, filenames in os.walk(dir):
51  for f in filenames:
52  fp = os.path.join(dirpath, f)
53  total += os.stat(fp).st_size
54  return total
55 
56 
57 def get_size(where, frameworks):
58  build_dir = 'src/objective-c/examples/Sample/Build/Build-%s/' % where
59  if not frameworks:
60  link_map_filename = 'Build/Intermediates.noindex/Sample.build/Release-iphoneos/Sample.build/Sample-LinkMap-normal-arm64.txt'
61  # IMPORTANT: order needs to match labels in _SIZE_LABELS
62  return parse_link_map(build_dir + link_map_filename)
63  else:
64  framework_dir = 'Build/Products/Release-iphoneos/Sample.app/Frameworks/'
65  core_size = dir_size(build_dir + framework_dir + 'grpc.framework')
66  objc_size = dir_size(build_dir + framework_dir + 'GRPCClient.framework') + \
67  dir_size(build_dir + framework_dir + 'RxLibrary.framework') + \
68  dir_size(build_dir + framework_dir + 'ProtoRPC.framework')
69  boringssl_size = dir_size(build_dir + framework_dir +
70  'openssl.framework')
71  protobuf_size = dir_size(build_dir + framework_dir +
72  'Protobuf.framework')
73  # a.k.a. "Total"
74  app_size = dir_size(build_dir +
75  'Build/Products/Release-iphoneos/Sample.app')
76  # IMPORTANT: order needs to match labels in _SIZE_LABELS
77  return core_size, objc_size, boringssl_size, protobuf_size, app_size
78 
79 
80 def build(where, frameworks):
81  subprocess.check_call(['make', 'clean'])
82  shutil.rmtree('src/objective-c/examples/Sample/Build/Build-%s' % where,
83  ignore_errors=True)
84  subprocess.check_call(
85  'CONFIG=opt EXAMPLE_PATH=src/objective-c/examples/Sample SCHEME=Sample FRAMEWORKS=%s ./build_one_example.sh'
86  % ('YES' if frameworks else 'NO'),
87  shell=True,
88  cwd='src/objective-c/tests')
89  os.rename('src/objective-c/examples/Sample/Build/Build',
90  'src/objective-c/examples/Sample/Build/Build-%s' % where)
91 
92 
93 def _render_row(new, label, old):
94  """Render row in 3-column output format."""
95  try:
96  formatted_new = '{:,}'.format(int(new))
97  except:
98  formatted_new = new
99  try:
100  formatted_old = '{:,}'.format(int(old))
101  except:
102  formatted_old = old
103  return '{:>15}{:>15}{:>15}\n'.format(formatted_new, label, formatted_old)
104 
105 
106 def _diff_sign(new, old, diff_threshold=None):
107  """Generate diff sign based on values"""
108  diff_sign = ' '
109  if diff_threshold is not None and abs(new_size[i] -
110  old_size[i]) >= diff_threshold:
111  diff_sign += '!'
112  if new > old:
113  diff_sign += '(>)'
114  elif new < old:
115  diff_sign += '(<)'
116  else:
117  diff_sign += '(=)'
118  return diff_sign
119 
120 
121 text = 'Objective-C binary sizes\n'
122 for frameworks in [False, True]:
123  build('new', frameworks)
124  new_size = get_size('new', frameworks)
125  old_size = None
126 
127  if args.diff_base:
128  old = 'old'
129  where_am_i = subprocess.check_output(
130  ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()
131  subprocess.check_call(['git', 'checkout', '--', '.'])
132  subprocess.check_call(['git', 'checkout', args.diff_base])
133  subprocess.check_call(['git', 'submodule', 'update', '--force'])
134  try:
135  build('old', frameworks)
136  old_size = get_size('old', frameworks)
137  finally:
138  subprocess.check_call(['git', 'checkout', '--', '.'])
139  subprocess.check_call(['git', 'checkout', where_am_i])
140  subprocess.check_call(['git', 'submodule', 'update', '--force'])
141 
142  text += ('********************FRAMEWORKS****************\n' if frameworks
143  else '**********************STATIC******************\n')
144  text += _render_row('New size', '', 'Old size')
145  if old_size == None:
146  for i in range(0, len(_SIZE_LABELS)):
147  if i == len(_SIZE_LABELS) - 1:
148  # skip line before rendering "Total"
149  text += '\n'
150  text += _render_row(new_size[i], _SIZE_LABELS[i], '')
151  else:
152  has_diff = False
153  # go through all labels but "Total"
154  for i in range(0, len(_SIZE_LABELS) - 1):
155  if abs(new_size[i] - old_size[i]) >= _DIFF_THRESHOLD:
156  has_diff = True
157  diff_sign = _diff_sign(new_size[i],
158  old_size[i],
159  diff_threshold=_DIFF_THRESHOLD)
160  text += _render_row(new_size[i], _SIZE_LABELS[i] + diff_sign,
161  old_size[i])
162 
163  # render the "Total"
164  i = len(_SIZE_LABELS) - 1
165  diff_sign = _diff_sign(new_size[i], old_size[i])
166  # skip line before rendering "Total"
167  text += '\n'
168  text += _render_row(new_size[i], _SIZE_LABELS[i] + diff_sign,
169  old_size[i])
170  if not has_diff:
171  text += '\n No significant differences in binary sizes\n'
172  text += '\n'
173 
174 print(text)
175 
176 check_on_pr.check_on_pr('ObjC Binary Size', '```\n%s\n```' % text)
http2_test_server.format
format
Definition: http2_test_server.py:118
binary_size.get_size
def get_size(where, frameworks)
Definition: binary_size.py:57
capstone.range
range
Definition: third_party/bloaty/third_party/capstone/bindings/python/capstone/__init__.py:6
build
Definition: build.py:1
xds_interop_client.int
int
Definition: xds_interop_client.py:113
binary_size._render_row
def _render_row(new, label, old)
Definition: binary_size.py:93
binary_size.build
def build(where, frameworks)
Definition: binary_size.py:80
grpc._common.decode
def decode(b)
Definition: grpc/_common.py:75
binary_size.dir_size
def dir_size(dir)
Definition: binary_size.py:48
binary_size._diff_sign
def _diff_sign(new, old, diff_threshold=None)
Definition: binary_size.py:106
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:37