From 751c6942f9f2c3e918c015d23d9245935ed8588f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 26 Oct 2019 18:19:32 +0530 Subject: [PATCH] Implement profiling of book preparation Cache href_to_name() which was marked as a hot function by the profiler. --- src/calibre/ebooks/oeb/polish/container.py | 8 +++++- src/calibre/srv/render_book.py | 33 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 9ec894a797..1cadb8986c 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -60,6 +60,7 @@ exists, join, relpath = os.path.exists, os.path.join, os.path.relpath OEB_FONTS = {guess_type('a.ttf'), guess_type('b.otf'), guess_type('a.woff'), 'application/x-font-ttf', 'application/x-font-otf', 'application/font-sfnt'} OPF_NAMESPACES = {'opf':OPF2_NS, 'dc':DC11_NS} +null = object() class CSSPreProcessor(cssp): @@ -261,6 +262,7 @@ class Container(ContainerBase): # {{{ self.pretty_print = set() self.cloned = False self.cache_names = ('parsed_cache', 'mime_map', 'name_path_map', 'encoding_map', 'dirtied', 'pretty_print') + self.href_to_name_cache = {} if clone_data is not None: self.cloned = True @@ -524,7 +526,11 @@ class Container(ContainerBase): # {{{ Convert an href (relative to base) to a name. base must be a name or None, in which case self.root is used. ''' - return href_to_name(href, self.root, base=base) + key = href, base + ans = self.href_to_name_cache.get(key, null) + if ans is null: + ans = self.href_to_name_cache[key] = href_to_name(href, self.root, base=base) + return ans def name_to_href(self, name, base=None): '''Convert a name to a href relative to base, which must be a name or diff --git a/src/calibre/srv/render_book.py b/src/calibre/srv/render_book.py index b6e07ba6b2..d03c7cf6a9 100644 --- a/src/calibre/srv/render_book.py +++ b/src/calibre/srv/render_book.py @@ -880,5 +880,36 @@ def viewer_main(): render_for_viewer(*args) +class Profiler(object): + + def __init__(self): + try: + import cProfile as profile + except ImportError: + import profile + self.profile = profile.Profile() + + def __enter__(self): + self.profile.enable() + + def __exit__(self, *a): + self.profile.disable() + self.profile.create_stats() + import pstats + stats = pstats.Stats(self.profile) + stats.sort_stats('cumulative') + stats.print_stats(.05) + + +def profile(): + from calibre.ptempfile import TemporaryDirectory + path = sys.argv[-1] + with TemporaryDirectory() as tdir, Profiler(): + return render( + path, tdir, serialize_metadata=True, + extract_annotations=True, virtualize_resources=False, max_workers=1 + ) + + if __name__ == '__main__': - render_for_viewer(sys.argv[-2], sys.argv[-1], None) + profile()