{"action":"created","issue":{"url":"https://api.github.com/repos/tiangolo/fastapi/issues/360","repository_url":"https://api.github.com/repos/tiangolo/fastapi","labels_url":"https://api.github.com/repos/tiangolo/fastapi/issues/360/labels{/name}","comments_url":"https://api.github.com/repos/tiangolo/fastapi/issues/360/comments","events_url":"https://api.github.com/repos/tiangolo/fastapi/issues/360/events","html_url":"https://github.com/tiangolo/fastapi/issues/360","id":463063205,"node_id":"MDU6SXNzdWU0NjMwNjMyMDU=","number":360,"title":"[QUESTION] enhance serialization speed","user":{"login":"euri10","id":1104190,"node_id":"MDQ6VXNlcjExMDQxOTA=","avatar_url":"https://avatars3.githubusercontent.com/u/1104190?v=4","gravatar_id":"","url":"https://api.github.com/users/euri10","html_url":"https://github.com/euri10","followers_url":"https://api.github.com/users/euri10/followers","following_url":"https://api.github.com/users/euri10/following{/other_user}","gists_url":"https://api.github.com/users/euri10/gists{/gist_id}","starred_url":"https://api.github.com/users/euri10/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/euri10/subscriptions","organizations_url":"https://api.github.com/users/euri10/orgs","repos_url":"https://api.github.com/users/euri10/repos","events_url":"https://api.github.com/users/euri10/events{/privacy}","received_events_url":"https://api.github.com/users/euri10/received_events","type":"User","site_admin":false},"labels":[{"id":1154536363,"node_id":"MDU6TGFiZWwxMTU0NTM2MzYz","url":"https://api.github.com/repos/tiangolo/fastapi/labels/question","name":"question","color":"d876e3","default":true}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2019-07-02T07:19:27Z","updated_at":"2019-07-04T14:36:24Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"**Description**\r\n\r\nI have to return sometimes big objects, I'm constrained in the fact that chunking them is not an option\r\n\r\nAn example of such an object would be a dict `{\"key:: value}` where value is a list of list, 20 list of 10k elements.\r\n\r\nI wrote this simple [test case](https://github.com/euri10/fastapi/blob/slow_serial/tests/test_serial_speed.py) that shows quite clearly the massive hit in several scenarios (run with `pytest tests/test_serial_speed.py --log-cli-level=INFO`) \r\nHere's the output:\r\n```\r\n======================================================================================================================= 1 passed in 3.68 seconds =======================================================================================================================\r\n(fastapi) ➜  fastapi git:(slow_serial) ✗ pytest tests/test_serial_speed.py --log-cli-level=INFO\r\n========================================================================================================================= test session starts ==========================================================================================================================\r\nplatform linux -- Python 3.6.8, pytest-5.0.0, py-1.8.0, pluggy-0.12.0\r\nrootdir: /home/lotso/PycharmProjects/fastapi\r\nplugins: cov-2.7.1\r\ncollected 1 item                                                                                                                                                                                                                                                       \r\n\r\ntests/test_serial_speed.py::test_routes \r\n---------------------------------------------------------------------------------------------------------------------------- live log call -----------------------------------------------------------------------------------------------------------------------------\r\nINFO     tests.test_serial_speed:test_serial_speed.py:39 route1: 0.05402565002441406\r\nINFO     tests.test_serial_speed:test_serial_speed.py:18 app.tests.test_serial_speed.route1, 9.395180225372314, ['http_status:200', 'http_method:GET', 'time:wall']\r\nINFO     tests.test_serial_speed:test_serial_speed.py:18 app.tests.test_serial_speed.route1, 9.395131000000001, ['http_status:200', 'http_method:GET', 'time:cpu']\r\nINFO     tests.test_serial_speed:test_serial_speed.py:52 route1: 0.049863576889038086\r\nINFO     tests.test_serial_speed:test_serial_speed.py:18 app.tests.test_serial_speed.route2, 10.358616590499878, ['http_status:200', 'http_method:GET', 'time:wall']\r\nINFO     tests.test_serial_speed:test_serial_speed.py:18 app.tests.test_serial_speed.route2, 10.358592000000002, ['http_status:200', 'http_method:GET', 'time:cpu']\r\nINFO     tests.test_serial_speed:test_serial_speed.py:64 route1: 0.05589580535888672\r\nINFO     tests.test_serial_speed:test_serial_speed.py:18 app.tests.test_serial_speed.route3, 11.318845272064209, ['http_status:200', 'http_method:GET', 'time:wall']\r\nINFO     tests.test_serial_speed:test_serial_speed.py:18 app.tests.test_serial_speed.route3, 11.318446000000002, ['http_status:200', 'http_method:GET', 'time:cpu']\r\nPASSED                                                                                                                                                                                                                                                           [100%]\r\n\r\n====================================================================================================================== 1 passed in 31.60 seconds =======================================================================================================================\r\n```\r\n\r\nall routes do the same with slight variations:\r\n\r\n1. build a big object, it's a dict, one key and a list of list value with 3 sublists of 100k elements, a little bit extreme maybe but it's to show clearly the impact\r\n2. return the object\r\n\r\nas you can see the time taken to build such an object is small, around 0.05s, but...\r\n\r\nroute1 just returns it, it takes 9s\r\nroute2 returns it but has the `response_model=BigData` in the signature, it takes 1s more\r\nroute3 is not intuitive to me, I thought that by already building a BigData object and returning it, there would be no penalty, but it's again slower\r\n\r\nHow can I [...]?\r\nimprove performance\r\n\r\n\r\nedit: the tests are available at this branch, can PR should you want to https://github.com/euri10/fastapi/tree/slow_serial\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/tiangolo/fastapi/issues/comments/508504233","html_url":"https://github.com/tiangolo/fastapi/issues/360#issuecomment-508504233","issue_url":"https://api.github.com/repos/tiangolo/fastapi/issues/360","id":508504233,"node_id":"MDEyOklzc3VlQ29tbWVudDUwODUwNDIzMw==","user":{"login":"jekirl","id":2546697,"node_id":"MDQ6VXNlcjI1NDY2OTc=","avatar_url":"https://avatars3.githubusercontent.com/u/2546697?v=4","gravatar_id":"","url":"https://api.github.com/users/jekirl","html_url":"https://github.com/jekirl","followers_url":"https://api.github.com/users/jekirl/followers","following_url":"https://api.github.com/users/jekirl/following{/other_user}","gists_url":"https://api.github.com/users/jekirl/gists{/gist_id}","starred_url":"https://api.github.com/users/jekirl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jekirl/subscriptions","organizations_url":"https://api.github.com/users/jekirl/orgs","repos_url":"https://api.github.com/users/jekirl/repos","events_url":"https://api.github.com/users/jekirl/events{/privacy}","received_events_url":"https://api.github.com/users/jekirl/received_events","type":"User","site_admin":false},"created_at":"2019-07-04T14:36:24Z","updated_at":"2019-07-04T14:36:24Z","author_association":"CONTRIBUTOR","body":"Just to confirm, you do have `ujson` installed? What type of machine are you running this on?  \r\n\r\nRoute 1 is going to be fastest as it is just returning the JSONResponse directly. \r\n\r\nRoute 2 is taking the dict, running `field.validate`   and then dumping it back out as JSON, so will be slower than route 1. \r\n\r\n_I  could be wrong on this one, have never really looked at the response portion of the fastapi code, but from a quick glance_\r\nRoute 3 constructs the pydantic model (which validates it), then calls `field.validate` on it again  and then dumps it back out as json, making it the slowest.\r\n\r\n\r\nFWIW, with ujson on a MBP:\r\n```test_serial_speed.py::test_routes\r\n------------------------------------------------------------------------------------------------ live log call -------------------------------------------------------------------------------------------------\r\ntest_serial_speed.py        41 INFO     route1: 0.028443098068237305\r\ntest_serial_speed.py        19 INFO     app.test_serial_speed.route1, 2.768159866333008, ['http_status:200', 'http_method:GET', 'time:wall']\r\ntest_serial_speed.py        19 INFO     app.test_serial_speed.route1, 2.7664400000000002, ['http_status:200', 'http_method:GET', 'time:cpu']\r\ntest_serial_speed.py        53 INFO     route2: 0.02795100212097168\r\ntest_serial_speed.py        19 INFO     app.test_serial_speed.route2, 3.3919589519500732, ['http_status:200', 'http_method:GET', 'time:wall']\r\ntest_serial_speed.py        19 INFO     app.test_serial_speed.route2, 3.386338999999999, ['http_status:200', 'http_method:GET', 'time:cpu']\r\ntest_serial_speed.py        64 INFO     route3: 0.03644418716430664\r\ntest_serial_speed.py        19 INFO     app.test_serial_speed.route3, 3.939689874649048, ['http_status:200', 'http_method:GET', 'time:wall']\r\ntest_serial_speed.py        19 INFO     app.test_serial_speed.route3, 3.9318400000000002, ['http_status:200', 'http_method:GET', 'time:cpu']```"}}
{"actor":{"display_login":"jekirl"}}