#36520: Performance Regression in parse_header_params
-----------------------------+-----------------------------------------
Reporter: David Smith | Type: Uncategorized
Status: new | Component: HTTP handling
Version: 5.2 | Severity: Normal
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+-----------------------------------------
https://github.com/django/django/pull/18424 / #35440 simplified the
`parse_header_params()` function.
This introduced a performance regression than can be seen on django-asv.
[https://django.github.io/django-asv/regressions.xml link] shows 5
performance regressions on or arround 28 March 2025, all of which relate
to this commit.
A minimal reproduction shows a 5x performance regression.
Before:
{{{
In [2]: %timeit parse_header_parameters("text/plain")
500 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops
each)
}}}
After
{{{
In [2]: %timeit parse_header_parameters("text/plain")
2.65 μs ± 44.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops
each)
}}}
Running a [https://pypi.org/project/line-profiler/#quick-start-modern
line-profile] shows:
{{{
Line # Hits Time Per Hit % Time Line Contents
==============================================================
320 @line_profiler.profile
321 def
parse_header_parameters(line, max_length=MAX_HEADER_LENGTH):
322 """
323 Parse a Content-type
like header.
324 Return the main
content-type and a dictionary of options.
325
326 If `line` is longer
than `max_length`, `ValueError` is raised.
327 """
328 1 11.0 11.0 2.4 if max_length is not
None and line and len(line) > max_length:
329 raise
ValueError("Unable to parse header parameters (value too long).")
330
331 1 47.0 47.0 10.3 m = Message()
332 1 51.0 51.0 11.2 m["content-type"] =
line
333 1 332.0 332.0 73.0 params =
m.get_params()
334
335 1 2.0 2.0 0.4 pdict = {}
336 1 7.0 7.0 1.5 key =
params.pop(0)[0].lower()
337 1 2.0 2.0 0.4 for name, value in
params:
338 if not name:
339 continue
340 if
isinstance(value, tuple):
341 value =
collapse_rfc2231_value(value)
342 pdict[name] =
value
343 1 3.0 3.0 0.7 return key, pdict
}}}
As I think this function is called with every request, I thought it worth
raising.
--
Ticket URL: <https://code.djangoproject.com/ticket/36520>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/01070198345276d8-02273378-aaca-4507-99d2-de930e447b66-000000%40eu-central-1.amazonses.com.