#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.

Reply via email to