From 56cba2d659d15129a665a3f540efc85c2b704d12 Mon Sep 17 00:00:00 2001 From: Rasmus Andersson Date: Thu, 25 Mar 2021 10:49:12 -0700 Subject: [PATCH] tooling: adds a --profile= option to fontbuild for profiling runs and adds misc/tools/fmtprofile.py for printing and inspecting profile results --- CONTRIBUTING.md | 31 +++++++++++++++++++++++++++++++ misc/fontbuild | 22 +++++++++++++++++++++- misc/tools/fmtprofile.py | 24 ++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100755 misc/tools/fmtprofile.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f98e933f0..3bf410f14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -180,6 +180,37 @@ Type `misc/tools/kernsample.py -h` for help on how to use the program. This only includes existing kerning and is thus only useful for adjustments. Additions must still be done manually. +### Performance profiling + +`fontbuild` has a `--profile=` option built in which when provided profiles the execution +and writes a pstat file. Example: + +``` +misc/fontbuild --profile=build/tmp/1.pstat compile -o build/tmp/f.otf build/ufo/Inter-Regular.ufo +``` + +You can print pstat files with the `fmtprofile.py` tool: + +``` +misc/tools/fmtprofile.py -n 20 build/tmp/1.pstat +``` + +You can inspect pstat files interactively with the `pstats` module: + +``` +python3 -m pstats build/tmp/1.pstat +``` + +For profiling Python programs that are not fontbuild, you can do this: + +``` +python -m cProfile -o 1.pstats -s time script.py +``` + +See for more information about profiling +Python programs. + + ### Miscellaneous tools There are several tools included with Inter to help "wrangle" metrics, generate glyphs, create PDFs and so on. You can find these tools in the `misc/tools` directory. They are all command-line tools and their usage can be queried by providing the help flag `-h`. diff --git a/misc/fontbuild b/misc/fontbuild index 170057d1a..c649fc5e1 100755 --- a/misc/fontbuild +++ b/misc/fontbuild @@ -88,6 +88,9 @@ class Main(object): argparser.add_argument('-q', '--quiet', action='store_true', help='Only print errors') + argparser.add_argument('--profile', metavar='', + help='Run in profiler for debugging, writing pstats data to ') + argparser.add_argument('-C', metavar='', dest='chdir', help='Run as if %(prog)s started in instead of the '+\ 'current working directory.') @@ -126,7 +129,24 @@ class Main(object): cmd = 'cmd_' + args.command.replace('-', '_') if not hasattr(self, cmd): fatal('Unrecognized command %s. Try --help' % args.command) - getattr(self, cmd)(argv[i:]) + cmdfn = getattr(self, cmd) + if args.profile: + try: + import cProfile as profile + except: + import profile + import __main__ + __main__.__dict__["cmdfn"] = cmdfn + __main__.__dict__["argv"] = argv[i:] + profile.run('cmdfn(argv)', args.profile) + print("") + print("profile saved to %r. You can now inspect it with for example:" % + args.profile) + print("misc/tools/fmtprofile.py -n 20 %r" % args.profile) + print("python3 -m pstats %r" % args.profile) + print("") + else: + cmdfn(argv[i:]) diff --git a/misc/tools/fmtprofile.py b/misc/tools/fmtprofile.py new file mode 100755 index 000000000..695d88ee7 --- /dev/null +++ b/misc/tools/fmtprofile.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# encoding: utf8 +# +# Formats a Python profile dump from for example `fontbuild --profile=file ...` +# +import argparse, pstats +from pstats import SortKey + +def main(): + argparser = argparse.ArgumentParser(description='Formats a Python profile dump') + argparser.add_argument('infile', metavar='', type=str, help='Python pstats file') + argparser.add_argument('-n', '--limit', metavar='N', default=None, type=int, + help='Only print the top N entries') + argparser.add_argument('--sort', metavar='', default=['time'], nargs='+', type=str, + help='Sort by keys (default is time.) Available keys: ' + ', '.join(SortKey)) + args = argparser.parse_args() + p = pstats.Stats(args.infile) + p.strip_dirs() + p.sort_stats(SortKey(*args.sort)) + p.print_stats(args.limit) + + +if __name__ == '__main__': + main()