Python Serverless on AWS Lambda · Python on Lambda: Runtime Fundamentals
Import Time, Heavy Libraries and SnapStart
Why importing pandas makes every cold start slow even for requests that never use it, and what to do about it.
Scenario
Example app: a reporting API function imports pandas, numpy and a large internal analytics module at the top of the file. Only the rarely used /export route needs them; most requests just read one record from DynamoDB.
What you see in production
Cold starts take 2 to 3 seconds, and because traffic is spiky, a noticeable share of users hit them. The Init Duration in logs is far larger than the handler duration.
The naive code
import pandas as pd # imported on every cold start
import numpy as np
from analytics import heavy # pulls in more modules
def handler(event, context):
if event["rawPath"] == "/export":
return export_report(pd, event)
return get_record(event) # 95% of requests
Reproduce it
A model of import costs on a cold start, with eager and lazy imports:
IMPORT_MS = {"boto3": 180, "pandas": 900, "numpy": 250, "analytics": 400} # illustrative
ROUTES = ["/record"] * 19 + ["/export"] # 5% of requests export
def cold_start_ms(eager, route):
modules = ["boto3"] + (["pandas", "numpy", "analytics"] if eager or route == "/export" else [])
return sum(IMPORT_MS[m] for m in modules)
for eager in (True, False):
costs = [cold_start_ms(eager, r) for r in ROUTES]
print("eager" if eager else "lazy ", "| /record cold start:", costs[0], "ms | /export cold start:", costs[-1], "ms")
Output
eager | /record cold start: 1730 ms | /export cold start: 1730 ms lazy | /record cold start: 180 ms | /export cold start: 1730 ms
Root cause
Python executes top-level imports during the init phase, before the first request is served. Large libraries import many submodules and native extensions, so every cold start pays for them, even when the request does not need them. Init time is also billed.
The fix
- Import heavy modules lazily inside the code path that needs them; Python caches the module after the first import, so the cost is paid once per environment, only when needed.
- Split functions by workload: a lightweight API function and a separate export function (often asynchronous).
- Trim dependencies (do you need pandas, or would the
csvmodule do?), and avoid importing whole SDKs when one client is enough. - For latency-critical functions, use SnapStart (supported for Python 3.12 and later; snapshots the initialised environment, with some extra charges for Python) or provisioned concurrency.
- Measure with
python -X importtime -c "import handler"to see which imports cost the most.
def export_report(event):
import pandas as pd # loaded only on the export path, cached afterwards
...
Trade-offs
- Lazy imports move the cost to the first request that needs it; for frequently used modules eager import is better.
- SnapStart restores from a snapshot, so anything unique (random seeds, connections, credentials with expiry) must be refreshed after restore.
- Splitting functions adds deployment units but lets each be sized and scaled independently.
Detect it in production
Init Durationin REPORT log lines and the share of invocations that are cold starts.python -X importtimeprofiles in CI for the handler module.- p99 latency spikes at the start of traffic bursts.
Key points
- Top-level imports run on every cold start and are billed.
- Import heavy, rarely used libraries lazily; split heavy routes into separate functions.
- Profile imports; use SnapStart or provisioned concurrency for strict latency.
Further reading: Improving startup performance with Lambda SnapStart · Python docs: -X importtime
Practice questions
Why does a top-level `import pandas` slow down requests that never use pandas?
Answer
Top-level imports run during the init phase of every new execution environment, so every cold start pays for importing pandas and its dependencies before handling any request.
How do you find which imports make a Python cold start slow?
Answer
Run python -X importtime -c 'import handler' (ideally in a Lambda-like environment) and look at cumulative import times, then make heavy rarely used imports lazy or remove them.
What must you watch out for when enabling SnapStart?
Answer
The function resumes from a snapshot of initialised memory, so values that must be unique or fresh (random seeds, unique IDs, network connections, temporary credentials) need to be regenerated after restore, for example in runtime hooks.
Open in the app to mark lessons complete, take notes and track your progress.