1
- #!/usr/bin/env python
2
-
3
- import argparse
4
1
import re
5
- import sys
6
2
import os
7
- import os .path
8
- import glob
9
3
import subprocess
10
4
import collections
11
5
from operator import itemgetter
39
33
"q_" : "Generic Function"
40
34
}
41
35
42
- SHORTCUTS = {
43
- "O" : "bin/PerfTests_O" ,
44
- "Ounchecked" : "bin/PerfTests_Ounchecked" ,
45
- "Onone" : "bin/PerfTests_Onone" ,
46
- "dylib" : "lib/swift/macosx/x86_64/libswiftCore.dylib" ,
47
- }
48
-
49
36
GenericFunctionPrefix = "__TTSg"
50
37
51
38
SortedPrefixes = sorted (Prefixes )
52
39
SortedInfixes = sorted (Infixes )
53
40
41
+
54
42
def addFunction (sizes , function , startAddr , endAddr , groupByPrefix ):
55
43
if not function or startAddr == None or endAddr == None :
56
44
return
@@ -75,6 +63,7 @@ def addFunction(sizes, function, startAddr, endAddr, groupByPrefix):
75
63
else :
76
64
sizes [function ] += size
77
65
66
+
78
67
def flatten (* args ):
79
68
for x in args :
80
69
if hasattr (x , '__iter__' ):
@@ -83,6 +72,7 @@ def flatten(*args):
83
72
else :
84
73
yield x
85
74
75
+
86
76
def readSizes (sizes , fileName , functionDetails , groupByPrefix ):
87
77
# Check if multiple architectures are supported by the object file.
88
78
# Prefer arm64 if available.
@@ -146,6 +136,7 @@ def readSizes(sizes, fileName, functionDetails, groupByPrefix):
146
136
147
137
addFunction (sizes , currFunc , startAddr , endAddr , groupByPrefix )
148
138
139
+
149
140
def compareSizes (oldSizes , newSizes , nameKey , title ):
150
141
oldSize = oldSizes [nameKey ]
151
142
newSize = newSizes [nameKey ]
@@ -156,6 +147,7 @@ def compareSizes(oldSizes, newSizes, nameKey, title):
156
147
perc = "- "
157
148
print "%-26s%16s: %8d %8d %6s" % (title , nameKey , oldSize , newSize , perc )
158
149
150
+
159
151
def compareSizesOfFile (oldFiles , newFiles , allSections , listCategories ):
160
152
oldSizes = collections .defaultdict (int )
161
153
newSizes = collections .defaultdict (int )
@@ -195,12 +187,14 @@ def compareSizesOfFile(oldFiles, newFiles, allSections, listCategories):
195
187
compareSizes (oldSizes , newSizes , "__common" , sectionTitle )
196
188
compareSizes (oldSizes , newSizes , "__bss" , sectionTitle )
197
189
190
+
198
191
def listFunctionSizes (sizeArray ):
199
192
for pair in sorted (sizeArray , key = itemgetter (1 )):
200
193
name = pair [0 ]
201
194
size = pair [1 ]
202
195
print "%8d %s" % (size , name )
203
196
197
+
204
198
def compareFunctionSizes (oldFiles , newFiles ):
205
199
oldSizes = collections .defaultdict (int )
206
200
newSizes = collections .defaultdict (int )
@@ -263,176 +257,3 @@ def compareFunctionSizes(oldFiles, newFiles):
263
257
print "Total size of functions that got smaller: {}" .format (sizeDecrease )
264
258
print "Total size of functions that got bigger: {}" .format (sizeIncrease )
265
259
print "Total size change of functions present in both files: {}" .format (sizeIncrease - sizeDecrease )
266
-
267
- def main ():
268
- parser = argparse .ArgumentParser (
269
- formatter_class = argparse .RawDescriptionHelpFormatter ,
270
- description = """
271
- Compares code sizes of "new" files, taking "old" files as a reference.
272
-
273
- Environment variables:
274
- SWIFT_NEW_BUILDDIR The old build-dir
275
- E.g. $HOME/swift-work/build/Ninja-ReleaseAssert+stdlib-Release/swift-macosx-x86_64
276
- SWIFT_OLD_BUILDDIR The new build-dir
277
- E.g. $HOME/swift-reference/build/Ninja-ReleaseAssert+stdlib-Release/swift-macosx-x86_64
278
-
279
- How to specify files:
280
- 1) No files:
281
- Compares codesize of the PerfTests_* executables and the swiftCore dylib in the new and old build-dirs.
282
- Example:
283
- cmpcodesize
284
-
285
- 2) One or more paths relative to the build-dirs (can be a pattern):
286
- Compares the files in the new and old build-dirs.
287
- Aliases:
288
- O => bin/PerfTests_O
289
- Ounchecked => bin/PerfTests_Ounchecked
290
- Onone => bin/PerfTests_Onone
291
- dylib => lib/swift/macosx/x86_64/libswiftCore.dylib
292
- Examples:
293
- cmpcodesize Onone
294
- cmpcodesize benchmark/PerfTestSuite/O/*.o
295
-
296
- 3) Two files:
297
- Compares these two files (the first is the old file).
298
- Example:
299
- cmpcodesize test.o newversion.o
300
-
301
- 4) Two lists of files, separated by '--':
302
- Compares a set a files.
303
- Example:
304
- cmpcodesize olddir/*.o -- newdir/*.o
305
-
306
- 5) One file (only available with the -l option):
307
- Lists function sizes for that file
308
- Example:
309
- cmpcodesize -l test.o""" )
310
-
311
- # Optional arguments.
312
- parser .add_argument ('-a' , '--additional-sections' ,
313
- help = 'Show sizes of additional sections.' ,
314
- action = 'store_true' ,
315
- dest = 'all_sections' ,
316
- default = False )
317
- parser .add_argument ('-c' , '--category' ,
318
- help = 'Show functions by category.' ,
319
- action = 'store_true' ,
320
- dest = 'list_categories' ,
321
- default = False )
322
- parser .add_argument ('-l' , '--list' ,
323
- help = 'List all functions (can be a very long list). ' +
324
- 'Cannot be used in conjunction with ' +
325
- '--additional-sections or --category. ' +
326
- 'You must specify between one and two files ' +
327
- 'when using this option.' ,
328
- action = 'store_true' ,
329
- dest = 'list_functions' ,
330
- default = False )
331
- parser .add_argument ('-s' , '--summarize' ,
332
- help = 'Summarize the sizes of multiple files instead ' +
333
- 'of listing each file separately.' ,
334
- action = 'store_true' ,
335
- dest = 'sum_sizes' ,
336
- default = False )
337
-
338
- # Positional arguments.
339
- # These can be specififed in means beyond what argparse supports,
340
- # so we gather them in a list and parse them manually.
341
- parser .add_argument ('files' , nargs = '*' ,
342
- help = 'A list of old and new files.' )
343
-
344
- # argparse can't handle an '--' argument, so we replace it with
345
- # a custom identifier.
346
- separator_token = '*-*-*'
347
- parsed_arguments = parser .parse_args (
348
- [separator_token if arg == '--' else arg for arg in sys .argv [1 :]])
349
-
350
- if parsed_arguments .list_functions :
351
- # --list is mutually exclusive with both --additional-sections
352
- # and --category. argparse is only capable of expressing mutual
353
- # exclusivity among options, not among groups of options, so
354
- # we detect this case manually.
355
- assert (not parsed_arguments .all_sections and
356
- not parsed_arguments .list_categories ), \
357
- 'Incorrect usage: --list cannot be specified in conjunction ' + \
358
- 'with --additional-sections or --category.'
359
- # A file must be specified when using --list.
360
- assert parsed_arguments .files , \
361
- 'Incorrect usage: Must specify between one and two files when ' + \
362
- 'using --list, but you specified no files.'
363
-
364
- if separator_token in parsed_arguments .files :
365
- separator_index = parsed_arguments .files .index (separator_token )
366
- oldFiles = parsed_arguments .files [:separator_index ]
367
- newFiles = parsed_arguments .files [separator_index + 1 :]
368
- else :
369
- oldFileArgs = parsed_arguments .files
370
-
371
- oldBuildDir = os .environ .get ("SWIFT_OLD_BUILDDIR" )
372
- newBuildDir = os .environ .get ("SWIFT_NEW_BUILDDIR" )
373
-
374
- if not parsed_arguments .files :
375
- assert oldBuildDir and newBuildDir , \
376
- 'Incorrect usage: You must specify either a list of ' + \
377
- 'files, or have both $SWIFT_OLD_BUILDDIR and ' + \
378
- '$SWIFT_NEW_BUILDDIR environment variables set.\n ' + \
379
- '$SWIFT_OLD_BUILDDIR = {0}\n $SWIFT_NEW_BUILDDIR = {1}' .format (
380
- oldBuildDir , newBuildDir )
381
- oldFileArgs = SHORTCUTS .keys ()
382
-
383
- oldFiles = []
384
- newFiles = []
385
- numExpanded = 0
386
- for file in oldFileArgs :
387
- if file in SHORTCUTS :
388
- file = SHORTCUTS [file ]
389
-
390
- if not file .startswith ("./" ) and oldBuildDir and newBuildDir :
391
- oldExpanded = glob .glob (os .path .join (oldBuildDir , file ))
392
- newExpanded = glob .glob (os .path .join (newBuildDir , file ))
393
- if oldExpanded and newExpanded :
394
- oldFiles .extend (oldExpanded )
395
- newFiles .extend (newExpanded )
396
- numExpanded += 1
397
-
398
- if numExpanded != 0 and numExpanded != len (oldFileArgs ):
399
- sys .exit ("mix of expanded/not-expanded arguments" )
400
- if numExpanded == 0 :
401
- if len (oldFileArgs ) > 2 :
402
- sys .exit ("too many arguments" )
403
- oldFiles = oldFileArgs [0 :1 ]
404
- newFiles = oldFileArgs [1 :2 ]
405
-
406
- for file in (oldFiles + newFiles ):
407
- if not os .path .isfile (file ):
408
- sys .exit ("file " + file + " not found" )
409
-
410
- if parsed_arguments .list_functions :
411
- if not newFiles :
412
- sizes = collections .defaultdict (int )
413
- for file in oldFiles :
414
- readSizes (sizes , file , True , False )
415
- listFunctionSizes (sizes .items ())
416
- else :
417
- compareFunctionSizes (oldFiles , newFiles )
418
- else :
419
- print "%-26s%16s %8s %8s %s" % ("" , "Section" , "Old" , "New" , "Percent" )
420
- if parsed_arguments .sum_sizes :
421
- compareSizesOfFile (oldFiles , newFiles ,
422
- parsed_arguments .all_sections ,
423
- parsed_arguments .list_categories )
424
- else :
425
- if len (oldFiles ) != len (newFiles ):
426
- sys .exit ("number of new files must be the same of old files" )
427
-
428
- oldFiles .sort
429
- newFiles .sort
430
-
431
- for idx , oldFile in enumerate (oldFiles ):
432
- newFile = newFiles [idx ]
433
- compareSizesOfFile ([oldFile ], [newFile ],
434
- parsed_arguments .all_sections ,
435
- parsed_arguments .list_categories )
436
-
437
- if __name__ == '__main__' :
438
- main ()
0 commit comments