OpenAPITools / OpenAPITools/openapi-generator
[BUG] Slow deserialisation into objects in generated python sdk
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
Related Issues:
https://github.com/OpenAPITools/openapi-generator/issues/4181
Description
We are seeing slow performance when deserialising responses from API calls into model objects. The actual deserialisation from JSON to dict looks ok, but mapping the dict to model objects is slow
openapi-generator version
5.1.0
OpenAPI declaration file content or url
https://www.lusid.com/api/swagger/v0/swagger.json
Generation Details
java -jar openapi-generator-cli.jar generate \
-i $swagger_file \
-g python-legacy
Steps to reproduce
A sample repo to reproduce can be found at https://github.com/rizwansaeed/OpenAPIPerf, this uses the generated sdk which also be found at https://github.com/finbourne/lusid-sdk-python-preview
/data/lusid.json - OpenAPI spec
/data/response.json - sample response
/python/main.py - test to show the deserialisation performance
With this we see it takes ~ 24s to deserialise the response into model objects
47218117 function calls (45073006 primitive calls) in 24.371 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 24.371 24.371 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api/transaction_portfolios_api.py:3257(get_transactions)
1 0.000 0.000 24.371 24.371 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api/transaction_portfolios_api.py:3303(get_transactions_with_http_info)
1 0.000 0.000 24.371 24.371 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:327(call_api)
1 0.013 0.013 24.371 24.371 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:121(__call_api)
1 0.000 0.000 24.342 24.342 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:266(deserialize)
585025/1 0.948 0.000 24.061 24.061 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:288(__deserialize)
260006/1 1.968 0.000 24.061 24.061 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:664(__deserialize_model)
2 0.007 0.003 24.054 12.027 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:302(<listcomp>)
10000 0.105 0.000 18.360 0.002 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:307(<dictcomp>)
260006 0.160 0.000 15.923 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/configuration.py:245(get_default_copy)
260006 1.950 0.000 15.763 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/configuration.py:90(__init__)
9880228/8580198 3.630 0.000 11.581 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/configuration.py:224(__setattr__)
120000 0.211 0.000 7.601 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/models/perpetual_property.py:56(__init__)
120000 0.169 0.000 7.347 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/models/property_value.py:59(__init__)
260006 0.525 0.000 6.506 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/configuration.py:299(debug)
520012 0.347 0.000 4.970 0.000 /usr/local/Cellar/python@3.9/3.9.8/Frameworks/Python.framework/Versions/3.9/lib/python3.9/logging/__init__.py:1417(setLevel)
520012 2.748 0.000 4.440 0.000 /usr/local/Cellar/python@3.9/3.9.8/Frameworks/Python.framework/Versions/3.9/lib/python3.9/logging/__init__.py:1372(_clear_cache)
15002 0.012 0.000 3.460 0.000 /OpenAPIPerf/python/venv/lib/python3.9/site-packages/lusid/api_client.py:643(__deserialize_datetime)
I have tried this with the latest python (non-legacy) version and observe similar performance characteristics
Process finished with exit code 0
52273648 function calls (46098366 primitive calls) in 25.016 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 25.016 25.016 /lusid-sdk-python-preview/sdk/lusid/api_client.py:761(__call__)
1 0.000 0.000 25.016 25.016 /lusid-sdk-python-preview/sdk/lusid/api/transaction_portfolios_api.py:3039(__get_transactions)
1 0.000 0.000 25.016 25.016 /lusid-sdk-python-preview/sdk/lusid/api_client.py:774(call_with_http_info)
1 0.000 0.000 25.015 25.015 /lusid-sdk-python-preview/sdk/lusid/api_client.py:335(call_api)
1 0.000 0.000 25.015 25.015 /lusid-sdk-python-preview/sdk/lusid/api_client.py:118(__call_api)
1 0.000 0.000 25.008 25.008 /lusid-sdk-python-preview/sdk/lusid/api_client.py:290(deserialize)
585034/10 1.214 0.000 24.710 2.471 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:1357(validate_and_convert_types)
275008/1 0.476 0.000 24.710 24.710 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:1244(attempt_convert_item)
260006/1 0.926 0.000 24.710 24.710 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:1166(deserialize_model)
260006/1 0.634 0.000 24.710 24.710 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:1562(wrapped_init)
1 0.000 0.000 24.710 24.710 /lusid-sdk-python-preview/sdk/lusid/model/versioned_resource_list_of_transaction.py:118(__init__)
2015056/11 0.658 0.000 24.710 2.246 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:166(__setattr__)
2015056/11 0.935 0.000 24.710 2.246 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:347(__setitem__)
455020/5 3.066 0.000 24.710 4.942 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:106(set_attribute)
5000 0.061 0.000 24.422 0.005 /lusid-sdk-python-preview/sdk/lusid/model/transaction.py:142(__init__)
480045/170034 0.192 0.000 21.073 0.000 {built-in method builtins.setattr}
120000 0.474 0.000 13.597 0.000 /lusid-sdk-python-preview/sdk/lusid/model/perpetual_property.py:106(__init__)
120000 0.447 0.000 5.855 0.000 /lusid-sdk-python-preview/sdk/lusid/model/property_value.py:110(__init__)
1590070 1.895 0.000 4.245 0.000 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:621(get_simple_class)
15002 0.046 0.000 3.582 0.000 /lusid-sdk-python-preview/sdk/lusid/model_utils.py:1062(deserialize_primitive)
For reference, and whilst it is not an exact like for like comparison, the test in csharp/OpenAPIPerf/UnitTest1.cs deserialising the model objects in less than 1 second.
Suggest a fix
It looks like when the __deserialise function comes across a list or dictionary it sequentially iterates over each item, and when there are nested structured objects this exacerbates the issue.
Are there any quick wins/settings we can use to improve the performance?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the generated Python entry points named in the report: api_client.py deserialization, model_utils.py model conversion, and configuration.py initialization, then run python/main.py from the OpenAPIPerf reproduction. Profile the sample response and preserve the deserialized model results while demonstrating a materially faster runtime than the reported 24–25 seconds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100