Conversation
19edd42 to
3fd70a1
Compare
|
A simple benchmark script: from datetime import date, timedelta
import json, orjson, ujson
import timeit
from pydantic import BaseModel, TypeAdapter
class Response(BaseModel):
values: dict[int, dict[date, float]]
def make_response(n: int, m: int) -> Response:
base = date(2025, 1, 1)
values = {
i + 1: {base + timedelta(i + j): (i + j) / 12.3 for j in range(m)}
for i in range(n)
}
return Response(values=values)
ta = TypeAdapter(Response)
r = make_response(2, 20)
cases = {
"json": "json.dumps(ta.dump_python(r, mode='json'), ensure_ascii=False).encode()",
"ujson": "ujson.dumps(ta.dump_python(r, mode='json'), ensure_ascii=False).encode()",
"orjson": "orjson.dumps(ta.dump_python(r, mode='json'), option=orjson.OPT_NON_STR_KEYS)",
"pydantic": "ta.dump_json(r)",
}
for case, code in cases.items():
print(case, min(timeit.repeat(code, number=10000, globals=globals())))Results: |
There was a problem hiding this comment.
@eltoder, thanks you for working on this!
Looks good to me in general, but I think we should add a bit more tests:
test that it raises with Pydantic V1 models (when V1 installed or with(done)from pydantic.v1 import BaseModel)- I would add some tests with different data and values of
response_model_**parameters to show the resulted JSON comparing to result with regularResponseclass. Results should be equal except some cases whenjson.dumpsraises but Pydantic handles it without error. - show that response is validated using response model
This comment was marked as resolved.
This comment was marked as resolved.
|
@YuriiMotov thanks for taking a look! This PR was intentionally minimal just to demonstrate the idea. I'm happy to add more tests and update documentation if the fastapi team agrees on the approach, especially since this is a user visible feature. |
b339d32 to
964beb4
Compare
|
@YuriiMotov I addressed your comments. Please take another look. |
This comment was marked as resolved.
This comment was marked as resolved.
|
I forgot to inform - we discussed this idea with Sebastian and he said he'd like to take a look at this later as he has his own thoughts regarding the implementation. So, let's wait. |
|
@YuriiMotov sounds good. thanks for letting me know |
When using this response class, json serialization is done using Pydantic's built-in json serialization (`dump_json(r)`) instead of generating an intermediate dict that is later serialized using a json library (`json.dumps(dump_python(r))`). In my testing this is 3-4x faster than using the standard json library (the default) and 50% faster than using orjson, without requiring any extra dependencies. This also allows configuring serialization behavior per model using Pydantic's model_config.
964beb4 to
3e3a402
Compare
This comment was marked as resolved.
This comment was marked as resolved.
Merging this PR will not alter performance
Comparing |
|
Hi @YuriiMotov! Do you have any ETA on when this is going to be reviewed? |
Unfortunately no ETA |
|
This pull request has a merge conflict that needs to be resolved. |
|
@YuriiMotov what if I need a custom content type? |
I see what you mean.. As I see it, the ability to bypass This would also solve use cases like #14929 |
|
I agree that this should be controlled by the response class. This is what I did in this PR. Instead of a boolean attribute this is usually done with a marker class like Back on the original subject. Note that automatically enabling pydantic json serialization like in #14962 is a backwards incompatible change. Pydantic's serialization behavior is different from the previous behavior. |
|
@YuriiMotov it appears that #14962 was reverted. Please re-open this PR. EDIT: I take it back. The revert commit a0a2d84 is not on any branch. |
It was in this PR just to test performance impact one more time. That PR was closed |
I think I agree it might be breaking, but could you please share a use case you have in mind? |
When using this response class, json serialization is done using Pydantic's built-in json serialization (
dump_json(r)) instead of generating an intermediate dict that is later serialized using a json library (json.dumps(dump_python(r))).In my testing this is 3-4x faster than using the standard json library (the default) and 50% faster than using orjson, without requiring any extra dependencies. This also allows configuring serialization behavior per model using Pydantic's model_config.