Skip to content

Lazy-load zoneinfo to improve startup time#156

Open
angusholder wants to merge 3 commits into
mfogel:mainfrom
angusholder:defer-loading-zoneinfo
Open

Lazy-load zoneinfo to improve startup time#156
angusholder wants to merge 3 commits into
mfogel:mainfrom
angusholder:defer-loading-zoneinfo

Conversation

@angusholder

Copy link
Copy Markdown

Motivation

In Django, ORM model classes are instantiated immediately at startup, so any heavy operations in their init path slow down your manage.py CLI startup time. Currently, instantiating a TimeZoneField indirectly calls zoneinfo.available_timezones() twice (via ZoneInfoBackend), which performs a lot of IO and parsing. On my machines this adds 25-35ms to startup time. Not huge on its own, but Django startup times can easily build up with a few libraries being a little bit slow. I measured this with the following script:

import subprocess
import timeit


PYTHON = 'python3.12'

def baseline():
    subprocess.run([PYTHON, '-c', 'import zoneinfo'])

def slow():
    subprocess.run([PYTHON, '-c', 'import zoneinfo; _ = zoneinfo.available_timezones(); _ = zoneinfo.available_timezones()'])

if __name__ == '__main__':
    N = 20

    # Take the fastest reading of all N runs
    baseline_time = min(timeit.repeat(stmt=baseline, repeat=N, number=1))
    slow_time = min(timeit.repeat(stmt=slow, repeat=N, number=1))

    diff = slow_time - baseline_time

    print(f'baseline: {baseline_time * 1000:.3f}ms')
    print(f'slow: {slow_time * 1000:.3f}ms')
    print(f'Difference: {diff * 1000:.3f}ms')

Timing on Heztner CCX53

baseline: 34.565ms
slow: 69.756ms
Difference: 35.191ms

Timing on Apple M4 Pro

baseline: 36.593ms
slow: 62.454ms
Difference: 25.861ms

Solution

I fixed this by making the zoneinfo reads happen lazily. This required changing TimeZoneField's choices field to be a lazy object itself, which unfortunately did make the code a bit more complex.

I only touched the ZoneInfoBackend, and left PYTZBackend alone because I don't think even has this issue - a cursory look at its source shows it lists timezones directly in the Python file, so isn't performing IO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant