Skip to content

perf: defer building model validators until first use - #2784

Open
dmontagu wants to merge 1 commit into
googleapis:mainfrom
dmontagu:defer-model-build
Open

perf: defer building model validators until first use#2784
dmontagu wants to merge 1 commit into
googleapis:mainfrom
dmontagu:defer-model-build

Conversation

@dmontagu

@dmontagu dmontagu commented Jul 28, 2026

Copy link
Copy Markdown

Adds defer_build=True to the model_config of google.genai._common.BaseModel, so that each model's pydantic validator and serializer is built the first time the model is actually used rather than when google.genai is imported.

google.genai.types defines 424 pydantic models (each with a TypedDict mirror), 418 of which get a validator and a serializer built at import time today. Any single application uses a small fraction of them — a client doing text generation never touches the Live, Batch, Tuning or Caching families.

Measurements

macOS/arm64, google-genai 1.72.0, released pydantic 2.13.4 / pydantic-core 2.46.4 from PyPI, no pydantic plugins installed. RSS attributable to import google.genai, measured in-process as an ru_maxrss delta; fresh interpreter per sample, warm bytecode caches, both variants interleaved round-robin, median of 12 samples each:

Python import RSS import time
3.12 today 56.0 MB 398 ms
3.12 defer_build=True 49.3 MB 287 ms
−6.6 MB (−11.9%) −111 ms (−27.8%)
3.14 today 63.4 MB 390 ms
3.14 defer_build=True 57.0 MB 298 ms
−6.3 MB (−10.0%) −92 ms (−23.6%)

The deferred work comes back only for the models actually used, once each (Python 3.12):

today defer_build=True
first types.Part(...) 0.04 ms 2.3 ms
first types.Content(...) + types.GenerateContentConfig(...) 0.04 ms 11.0 ms
steady-state types.Part(...) 3.33 µs 3.26 µs
steady-state content.model_dump(exclude_none=True) 1.15 µs 1.14 µs
RSS after exercising all of the above 56.1 MB 50.9 MB

So an application pays roughly 13 ms once, at first use, for the model families it actually touches, and keeps most of the memory saving permanently. Validation and serialization throughput are unaffected — defer_build only moves when the validator is built, not what it does.

Compatibility

defer_build is a standard pydantic ConfigDict option, available since pydantic 2.1 (this package requires ≥ 2.9), and it is inherited by every BaseModel subclass in types.py. model_validate, model_dump and model_json_schema all trigger the build transparently on first use; __pydantic_core_schema__ is regenerated on demand if anything asks for it.

The full test suite passes with the change.

@google-cla

google-cla Bot commented Jul 28, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@Venkaiahbabuneelam Venkaiahbabuneelam self-assigned this Jul 29, 2026
@Venkaiahbabuneelam Venkaiahbabuneelam added the size:S Code changes < 10 lines label Jul 29, 2026
google.genai.types defines ~770 models, and importing the package builds a
pydantic validator and serializer for every one of them. Any single application
uses a small fraction: a client doing text generation never touches the Live,
Batch, Tuning or Caching families.

Setting defer_build=True on the shared BaseModel builds each model's validator
and serializer the first time that model is actually used instead. Measured on
Python 3.12 with google-genai 1.72.0, RSS attributable to `import google.genai`
drops from 84.1MB to 78.2MB (-7.0%) and import time from 0.73s to 0.57s (-22%),
against a one-off cost of ~5ms for the first model used and ~16ms for the first
GenerateContentConfig. Steady-state validation throughput is unchanged.
@dmontagu
dmontagu force-pushed the defer-model-build branch from 022f428 to 162cdaf Compare July 30, 2026 15:33
@dmontagu

Copy link
Copy Markdown
Author

@Venkaiahbabuneelam please feel free to take this over or close it and tweak in any way you like. Obviously it's only a one-line change besides the comment, my point is just I have zero need for attribution or anything like that.

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

Labels

size:S Code changes < 10 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants