From 7b6cf64763bf9457610acd69abdbd24149c40beb Mon Sep 17 00:00:00 2001 From: TheAMM Date: Mon, 8 Apr 2019 19:37:01 +0300 Subject: [PATCH] Cache url_for calls with lru_cache This commit adds a caching_url_for using functools.lru_cache (currently with maxsize at 4096 entries) and replaces flask.url_for with it, as there is no harm in doing so. This greatly improves template generation speed, from ~115ms to ~75ms on the front page (using the simple benchmark introduced in the previous commit). --- nyaa/__init__.py | 10 ++++++++++ nyaa/template_utils.py | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/nyaa/__init__.py b/nyaa/__init__.py index 33732e7..92aca21 100644 --- a/nyaa/__init__.py +++ b/nyaa/__init__.py @@ -8,9 +8,15 @@ from flask_assets import Bundle # noqa F401 from nyaa.api_handler import api_blueprint from nyaa.extensions import assets, cache, db, fix_paginate, toolbar from nyaa.template_utils import bp as template_utils_bp +from nyaa.template_utils import caching_url_for from nyaa.utils import random_string from nyaa.views import register_views +# Replace the Flask url_for with our cached version, since there's no real harm in doing so +# (caching_url_for has stored a reference to the OG url_for, so we won't recurse) +# Touching globals like this is a bit dirty, but nicer than replacing every url_for usage +flask.url_for = caching_url_for + def create_app(config): """ Nyaa app factory """ @@ -86,6 +92,10 @@ def create_app(config): app.jinja_env.lstrip_blocks = True app.jinja_env.trim_blocks = True + # The default jinja_env has the OG Flask url_for (from before we replaced it), + # so update the globals with our version + app.jinja_env.globals['url_for'] = flask.url_for + # Database fix_paginate() # This has to be before the database is initialized app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False diff --git a/nyaa/template_utils.py b/nyaa/template_utils.py index f7e64af..ff2904a 100644 --- a/nyaa/template_utils.py +++ b/nyaa/template_utils.py @@ -1,3 +1,4 @@ +import functools import os.path import re from datetime import datetime @@ -25,6 +26,28 @@ def create_magnet_from_es_torrent(): # ######################### TEMPLATE GLOBALS ######################### +flask_url_for = flask.url_for + + +@functools.lru_cache(maxsize=1024*4) +def _caching_url_for(endpoint, **values): + return flask_url_for(endpoint, **values) + + +@bp.app_template_global() +def caching_url_for(*args, **kwargs): + try: + # lru_cache requires the arguments to be hashable. + # Majority of the time, they are! But there are some small edge-cases, + # like our copypasted pagination, parameters can be lists. + # Attempt caching first: + return _caching_url_for(*args, **kwargs) + except TypeError: + # Then fall back to the original url_for. + # We could convert the lists to tuples, but the savings are marginal. + return flask_url_for(*args, **kwargs) + + @bp.app_template_global() def static_cachebuster(filename): """ Adds a ?t= cachebuster to the given path, if the file exists.