>_ josetilabs.com
← blog

10 Oct 2026 · python, performance, desenvolvimento · 4 min

Ten imports cost 100 ms of your Python script's startup

A script that imports ten standard library modules takes 102 ms to reach its first useful line. With the same list declared as lazy imports, 14 ms. The difference is one word in front of each import.

That has been available since October 9, when Python 3.15 shipped with PEP 810 implemented. The syntax is new and explicit: you write lazy import asyncio and the module only loads the first time something uses it. The proposal calls that step reification, and the practical effect is that startup stops paying for code that may never run.

What I measured

I built 3.14.8 and 3.15.0 with the same compiler and the same flags, then ran two scripts with identical contents. One imports ten standard library modules the old way, the other declares the same ten with lazy import and never touches them. Seven runs each, best time:

script 3.14.8 3.15.0
plain import, ten modules 103 ms 100 ms
lazy import, same ten modules syntax error 14 ms

3.14 will not accept the syntax at all, which is fair: lazy imports are a 3.15 feature. The number that matters is the bottom one, 14 ms, seven times smaller than the plain start. The module count explains it: the plain script finished with 230 modules in sys.modules, the lazy one finished with 32.

I also measured each import on its own, in a fresh interpreter per line:

module cost to import
asyncio 38.7 ms
urllib.request 28.6 ms
unittest 24.2 ms
logging.config 22.2 ms
email.message 14.3 ms
tarfile 10.9 ms
inspect 10.0 ms
statistics 9.4 ms
ssl 9.2 ms
json 7.2 ms
argparse 7.2 ms
re 5.5 ms
sqlite3 3.9 ms

Those numbers do not add up to the 100 ms of the full script, because several modules share dependencies and the same cost shows up in more than one line. The table is for picking candidates, not for summing a budget.

Where it pays

Command line tools are the obvious case. A CLI usually has a fast path, something like --version or --help, and a heavy path that does the work. The fast path pays for importing everything and exits in 100 ms when it could exit in 15. On a stateless server with a cold start per request, the same bill repeats multiplied by load.

python
# before
import argparse, asyncio, sqlite3, unittest

# after, on Python 3.15
lazy import argparse, asyncio, sqlite3, unittest

To pick what deserves to be lazy, measure before touching anything:

bash
python3.15 -X importtime your_cli.py 2> times.txt
sort -k2 -nr times.txt | head -15

The output lists every import with its cumulative time. The first three entries that the fast path does not use are your answer.

What changes in behavior

Lazy imports move the moment of failure, and that has consequences. If a module is missing, the ImportError surfaces at first use, not on the first line of the program. For anyone writing infrastructure scripts, that changes what the log shows when a dependency disappears from a machine.

Import side effects are delayed too. A module that configures logging, registers a plugin or opens a connection on import now does that in the middle of execution, and where that happens stops being predictable. A module with import-time effects is a bad candidate for the list.

The proposal handles both with a filter mechanism and a global mode that apply the rule without touching the code. That is more powerful and more dangerous, because it now covers third party libraries that were never written with this in mind. Starting with the imports you control yourself is the boring version that works.

Caveat: the measurement comes from two locally built interpreters, with my chosen module list, over seven runs. The gain depends on what is being imported and how much of the fast path needs it. import time has nothing to gain.

My read is that the feature is worth more for the diagnosis it forces than for the win itself. Every CLI I have written loads things it never uses, and I never measured it because startup felt instant. A hundred milliseconds at a time, multiplied across every call of an automation script, adds up to seconds of waiting nobody blamed on imports. It is the usual trade, buying startup time with code discipline, and it shows up in any runtime where you swap resources for latency or pay for pauses nobody sees.

Read next

// By e-mail

Posts published here also go out by e-mail. Delivery has not started yet: whoever leaves an address now joins the first batch.

I store only your e-mail, the date and the record of this consent. No cookies, no trackers, nothing shared with third parties. Details in the privacy policy.