OpenAPITools / OpenAPITools/openapi-generator

[BUG] Slow deserialisation into objects in generated python sdk

Open
#11,496 15 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Client: Python Issue: Bug
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.