Opened 5 years ago

Closed 5 years ago

#30498 closed Cleanup/optimization (fixed)

lazy() class preparation is not being cached correctly.

Reported by: Ran Benita Owned by: Ran Benita
Component: Utilities Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Doing self.__prepared = True changes the instance, but the intention is to change the class variable: https://github.com/django/django/blob/888fdf182e164fa4b24aa82fa833c90a2b9bee7a/django/utils/functional.py#L82

This makes functions like gettext_lazy, format_lazy and reverse_lazy a lot slower than they ought to be.

Regressed in Django 1.8 (b4e76f30d12bfa8a53cc297c60055c6f4629cc4c).

Using this micro-benchmark on Python 3.7:

import cProfile
from django.utils.functional import lazy

def identity(x): return x
lazy_identity = lazy(identity, int)
cProfile.run("for i in range(10000): str(lazy_identity(1))")

Before:

          910049 function calls in 0.208 seconds

    Ordered by: standard name

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    0.010    0.010    0.208    0.208 <string>:1(<module>)
     10000    0.001    0.000    0.001    0.000 bench.py:4(identity)
     10000    0.005    0.000    0.010    0.000 functional.py:105(__str__)
     10000    0.004    0.000    0.188    0.000 functional.py:159(__wrapper__)
     10000    0.007    0.000    0.185    0.000 functional.py:76(__init__)
     10000    0.089    0.000    0.178    0.000 functional.py:83(__prepare_class__)
     10000    0.004    0.000    0.005    0.000 functional.py:99(__cast)
         1    0.000    0.000    0.208    0.208 {built-in method builtins.exec}
    840000    0.087    0.000    0.087    0.000 {built-in method builtins.hasattr}
        46    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     10000    0.002    0.000    0.002    0.000 {method 'mro' of 'type' objects}

After:

          50135 function calls in 0.025 seconds

    Ordered by: standard name

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    0.008    0.008    0.025    0.025 <string>:1(<module>)
     10000    0.001    0.000    0.001    0.000 bench.py:4(identity)
     10000    0.005    0.000    0.009    0.000 functional.py:105(__str__)
     10000    0.003    0.000    0.008    0.000 functional.py:159(__wrapper__)
     10000    0.005    0.000    0.005    0.000 functional.py:76(__init__)
         1    0.000    0.000    0.000    0.000 functional.py:83(__prepare_class__)
     10000    0.004    0.000    0.005    0.000 functional.py:99(__cast)
         1    0.000    0.000    0.025    0.025 {built-in method builtins.exec}
        84    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
        46    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
         1    0.000    0.000    0.000    0.000 {method 'mro' of 'type' objects}

Change History (3)

comment:2 by Mariusz Felisiak, 5 years ago

Owner: changed from nobody to Ran Benita
Status: newassigned
Summary: lazy() class preparation is not being cached correctlylazy() class preparation is not being cached correctly.
Triage Stage: UnreviewedAccepted

Thanks for the report! Reproduced at 888fdf182e164fa4b24aa82fa833c90a2b9bee7a.

comment:3 by Mariusz Felisiak <felisiak.mariusz@…>, 5 years ago

Resolution: fixed
Status: assignedclosed

In a2c31e12:

Fixed #30498 -- Fixed proxy class caching in lazy().

lazy() should prepare the proxy class only once (the first time it's
used) not on every call.

Regression in b4e76f30d12bfa8a53cc297c60055c6f4629cc4c.

Note: See TracTickets for help on using tickets.
Back to Top