From 203731e60a279bc6b4ac023a0e7b00fdda6719fe Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 2 May 2024 14:36:05 -0400 Subject: [PATCH] Performance measurement tools --- src/Mod/Test/TestPerf.py | 105 ++++++++++++++++++++++++++++++++++++++ tools/profile/perftest.sh | 36 +++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 src/Mod/Test/TestPerf.py create mode 100755 tools/profile/perftest.sh diff --git a/src/Mod/Test/TestPerf.py b/src/Mod/Test/TestPerf.py new file mode 100644 index 0000000000..954ff3d40b --- /dev/null +++ b/src/Mod/Test/TestPerf.py @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# *************************************************************************** +# * * +# * Copyright (c) 2024 bgbsww@gmail.com * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, but * +# * WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# *************************************************************************** + +import sys +import unittest +import FreeCAD as App +import Part + +try: + from guppy import hpy + + Memtest = True +except ImportError: + Memtest = False + +try: + import cProfile + + Pyprofile = True +except ImportError: + Pyprofile = False + + +class PerfTestCase(unittest.TestCase): + """ + Special Test Case that takes a list of filenames after the "--pass" parameter to FreeCAD, and + runs a performance test by opening them, starting instrumentation, calling recompute(), and + then saving results. + + Intended to be run as " FreeCAD -t TestPerf --pass + + External perf profiling requires Python 3.12 or better, and a linux platform. + cProfile profiling and guppy memory information can run anywhere. + """ + + def setUp(self): + if "--pass" in sys.argv: + self.fileList = sys.argv[sys.argv.index("--pass") + 1 :] + else: + raise FileNotFoundError("Must provide filename parameter(s) via --pass") + if Part.Shape().ElementMapVersion == "": + self.tnp = "" + else: + self.tnp = ".tnp" + if Memtest: + # Use filename of first model with ".mprofile" appended for python memory use info. + self.memfile = open(self.fileList[0] + self.tnp + ".mprofile", "w", encoding="utf-8") + + def testAll(self): + if Pyprofile: + # Generate a cProfile file as a python only time profile. + profile = cProfile.Profile() + profile.enable() + try: + # This is Python 3.12 on supported platforms ( linux ) only so that if we are run under + # an external 'perf' command, we report the python data. This can be extremely useful, + # because it contains not only time consumed, but python and c++ calls that took place + # so deep analysis can be performed on the resulting file. See calling script in + # tools/profile/perftest.sh for a wrapper. + sys.activate_stack_trampoline("perf") + except AttributeError: + pass # Totally okay if we don't have that, we can use the cProfile if it's there. + + # Walk all files after the --pass. Normally one to avoid result intermingling. + for fileName in self.fileList: + doc = App.openDocument(fileName) + doc.recompute() # The heart of the performance measurement. + if Memtest: + # If guppy is available, take a heap snapshot and save it. Note that if multiple + # files are provided then their heap data sets will be appended to the same file. + dumpdata = hpy().heap() + dumpdata.stat.dump(self.memfile) + self.memfile.flush() + App.closeDocument(doc.Name) + + try: + sys.deactivate_stack_trampoline() + except AttributeError: + pass + if Pyprofile: + profile.disable() + # Use filename of first model with ".cprofile" appended for python profiling information. + profile.dump_stats(self.fileList[0] + self.tnp + ".cprofile") + if Memtest: + self.memfile.close() diff --git a/tools/profile/perftest.sh b/tools/profile/perftest.sh new file mode 100755 index 0000000000..952d818c02 --- /dev/null +++ b/tools/profile/perftest.sh @@ -0,0 +1,36 @@ +#! /bin/bash + +# Example file to drive the performance profiling python test from the shell. +# Built to support the Topological Naming Problem fixes from early 2024; +# this will likely need tweaking for your use. + +notnp= #/bin/FreeCAD${cmd} +tnp= #>"${results}" +