Skip to content

Commit

Permalink
Optimize render_stacktrace()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
adamchainz committed Jan 12, 2022
1 parent 46b661f commit 4289268
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 25 deletions.

This file was deleted.

47 changes: 26 additions & 21 deletions debug_toolbar/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
(
'<span class="djdt-path">{}/</span>'
+ '<span class="djdt-file">{}</span> in'
+ ' <span class="djdt-func">{}</span>'
+ '(<span class="djdt-lineno">{}</span>)\n'
+ ' <span class="djdt-code">{}</span>\n'
),
directory,
filename,
func,
lineno,
code,
)
)
if show_locals:
html += format_html(
' <pre class="djdt-locals">{}</pre>\n',
pformat(locals_),
)
html += "\n"
return mark_safe(html)


def get_template_info():
Expand Down

0 comments on commit 4289268

Please sign in to comment.