From 3d77806ee0582e8cf8a89e91cb8c420d9552de21 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Wed, 12 Jan 2022 09:34:50 +0000 Subject: [PATCH] Optimize render_stacktrace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is called by the SQL panel to render each frame of each stack trace. Previously it used a Django template, which is slow. By generating the HTML with pure Python code, we can avoid much of this overhead. I tried this change on a demo app I built that has 96 total queries, due to N+1 queries. The queries execute in 13ms due to use of SQLite, so the whole view is a negligible concern. Nearly all the view runtime is the toolbar itself. Without this change, the runtime is ~1300ms; with the change it’s ~1100ms. That's a saving of **15%**. I also checked the appearance of the generated HTML hasn’t changed. --- .../debug_toolbar/panels/sql_stacktrace.html | 4 -- debug_toolbar/utils.py | 47 ++++++++++--------- docs/changes.rst | 1 + 3 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html b/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html deleted file mode 100644 index 426783b93..000000000 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_stacktrace.html +++ /dev/null @@ -1,4 +0,0 @@ -{% for s in stacktrace %}{{s.0}}/{{s.1}} in {{s.3}}({{s.2}}) - {{s.4}} - {% if show_locals %}
{{s.5|pprint}}
{% endif %} -{% endfor %} diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index c662ab113..57965c457 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -3,12 +3,12 @@ import re import sys from importlib import import_module -from itertools import chain +from pprint import pformat import django from django.core.exceptions import ImproperlyConfigured from django.template import Node -from django.template.loader import render_to_string +from django.utils.html import format_html from django.utils.safestring import mark_safe from debug_toolbar import settings as dt_settings @@ -69,26 +69,31 @@ def tidy_stacktrace(stack): def render_stacktrace(trace): - stacktrace = [] - for frame in trace: - params = (v for v in chain(frame[0].rsplit(os.path.sep, 1), frame[1:])) - params_dict = {str(idx): v for idx, v in enumerate(params)} - try: - stacktrace.append(params_dict) - except KeyError: - # This frame doesn't have the expected format, so skip it and move - # on to the next one - continue - - return mark_safe( - render_to_string( - "debug_toolbar/panels/sql_stacktrace.html", - { - "stacktrace": stacktrace, - "show_locals": dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"], - }, + show_locals = dt_settings.get_config()["ENABLE_STACKTRACES_LOCALS"] + html = "" + for abspath, lineno, func, code, locals_ in trace: + directory, filename = abspath.rsplit(os.path.sep, 1) + html += format_html( + ( + '{}/' + + '{} in' + + ' {}' + + '({})\n' + + ' {}\n' + ), + directory, + filename, + func, + lineno, + code, ) - ) + if show_locals: + html += format_html( + '
{}
\n', + pformat(locals_), + ) + html += "\n" + return mark_safe(html) def get_template_info(): diff --git a/docs/changes.rst b/docs/changes.rst index cacbfa5af..5433326c1 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -11,6 +11,7 @@ Next version * Reset settings when overridden in tests. Packages or projects using django-debug-toolbar can now use Django’s test settings tools, like ``@override_settings``, to reconfigure the toolbar during tests. +* Optimize rendering of SQL panel, saving about 15% of its run time. 3.2.4 (2021-12-15) ------------------