To reproduce, download the .json files with raw responses from here.
Running in standard mode
uv run oai_pydantic.pyProcessing oai_response_1024.json...
model_validate taken 5.02ms
model_construct taken 5.86ms
model_validate_json taken 2.18ms
Processing oai_response_8192.json...
model_validate taken 12.85ms
model_construct taken 37.14ms
model_validate_json taken 15.86ms
Processing oai_response_16384.json...
model_validate taken 54.59ms
model_construct taken 52.33ms
model_validate_json taken 31.51ms
Processing oai_response_32768.json...
model_validate taken 127.27ms
model_construct taken 133.14ms
model_validate_json taken 63.51ms- Indeed,
model_validate_jsonis roughly 2x faster thanmodel_validateandmodel_construct. That's nice but this would still be a huge bottleneck in RL training with 4k+ batch sizes - As @baggiponte said, I am quite surprised that
model_validatealways takes ~same time asmodel_construct. Shouldn't the latter not do any validation? Maybe this is not true for submodels? If so, is there a way to also skip validation of submodels - all this data is entirely trusted so skipping validation entirely is fine - Nit: I am not quite sure why I am now measuring 130ms instead of the 180ms I got a couple of days ago on the 32k sample, maybe because I am using
perf_counternow instead of plaintimeor the CPU has a better day today, who knows. But really it's about the order of magnitude which is still the same. - Also, it does seem like constructing the Pydantic model scales ~linearly with the number of logprobs it has to parse.
Ignoring the logprobs field
uv run oai_pydantic.py --ignore-logprobsProcessing oai_response_1024.json...
model_validate taken 2.97ms
model_construct taken 3.38ms
model_validate_json taken 0.26ms
Processing oai_response_8192.json...
model_validate taken 0.03ms
model_construct taken 0.15ms
model_validate_json taken 0.10ms
Processing oai_response_16384.json...
model_validate taken 0.03ms
model_construct taken 0.13ms
model_validate_json taken 0.16ms
Processing oai_response_32768.json...
model_validate taken 0.03ms
model_construct taken 0.14ms
model_validate_json taken 0.26ms- It seems like it's only the logprob parsing that is taking a significant amount of time.
Using the hotfix that we use in prime-rl for now
uv run oai_pydantic.py --use-hotfixProcessing oai_response_1024.json...
model_validate taken 2.38ms
model_construct taken 3.03ms
model_validate_json taken 1.96ms
Processing oai_response_8192.json...
model_validate taken 0.17ms
model_construct taken 0.15ms
model_validate_json taken 13.58ms
Processing oai_response_16384.json...
model_validate taken 0.97ms
model_construct taken 0.14ms
model_validate_json taken 21.81ms
Processing oai_response_32768.json...
model_validate taken 1.83ms
model_construct taken 0.14ms
model_validate_json taken 43.73msOur hotfix essentially skips whatever Pydantic does to the logprobs field so we are still quick. Interestingly (I hadn't tested this before), model_validate_json does not seem to profit from it.
Super interested to hear where people think the bottleneck is and if we can find a more elegant general solution!:)
This analyis is based on my understanding of the code & libraries, please correct me if I am mistaken. Hope this helps :)
Concerns
I believe the benchmarking is not very accurate for a couple of reasons
BaseModelis constructed Pydantic spends some time to create its schema (https://github.com/pydantic/pydantic/blob/1a8850d101e67d2744ba8c6286e1172d7cd89d0b/pydantic/_internal/_model_construction.py#L641) which is cached for future uses. This is not being correctly accounted for in this benchmarking codemodel_validate/model_constructandmodel_validate_jsondoes not take into consideration the time required to parse the JSON.i.
model_validate/model_constructis doing one operation, creating theBaseModel.ii.
model_validate_jsonis doing two operations, parsing the JSON and creating theBaseModel.Updated Benchmarking
I have updated and attached the benchmarking code at the bottom.
Here are the results from my updated code which takes into account the time required to parse JSON for
model_validateandmodel_constructResults
--ignore-logprobs--use-hotfix--ignore-logprobsand--use-hotfixCode
oai_pydantic_v2.py
What's happening with
model_construct?The OpenAI Python SDK has it's own
BaseModel(https://github.com/openai/openai-python/blob/4e8856576211064b09c0cc4a1ed35b82b169abe2/src/openai/_models.py#L86) which implements a custommodel_constructcalledconstruct(https://github.com/openai/openai-python/blob/4e8856576211064b09c0cc4a1ed35b82b169abe2/src/openai/_models.py#L206) which is recursively constructing it's fields from the JSONdict. This is what is creating the additional latency. If it were to use the originalmodel_constructfrompydantic.BaseModelthe results would be vastly different (also the behavior would be different). Here are the results,Code
oai_model_construct.py
Thoughts
Given the nested structure of the response, the time required to build the
BaseModelis expected. If you don't want to spend this time you can simply use the results of.json()akadict[str, Any]which would probably be the fastest. If you want type hints but no validation or the overhead of creatingBaseModelyou can convert all these models toTypedDictwhich would give you type-hints in your IDE.