-
-
Notifications
You must be signed in to change notification settings - Fork 760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve import time #1846
Improve import time #1846
Conversation
06c3738
to
4ef9150
Compare
I doubt that uvicorn is dominating the startup time of most Python applications with a 60ms import time but 🤷♀️ this isn't too complex of a change. Curious what the maintainers think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @mgu ! I'll share my review here.
So... If you have to ask if a change is worth it, it means that you are aware that this improvement is subjective, and historically, on the framework, we tend to avoid changes that are not a clear improvement.
But some comments:
- The
supervisors
code is a breaking change, so I'd like to avoid that. - I think having
h11.DEFAULT_MAX_INCOMPLETE_EVENT_SIZE
exposed as default was intentional for the user to see which value is the default. This can be discussed tho... I think we took the decision on the PR that introduced it. - What's the current exception when you don't have
pyyaml
installed? Now it becomesModuleNotFound
, right? I think this one should be fine. 👍
@@ -344,7 +342,7 @@ def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> No | |||
"--h11-max-incomplete-event-size", | |||
"h11_max_incomplete_event_size", | |||
type=int, | |||
default=DEFAULT_MAX_INCOMPLETE_EVENT_SIZE, | |||
default=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I don't recall this wrong, I believe that when this was implemented None
was the default, but it was changed to the default value that we currently have. I can be wrong tho...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value was None before this PR. IMO exposing the default value was just a side effect of fixing the breaking change in h11.
There's indeed a trade-off between simplicity and performance in the change of I'll remove the change on
|
4ef9150
to
ab0a67c
Compare
How did you measure the improvement? |
import time
start = time.time()
import uvicorn.main
end = time.time()
print(end - start) I run this in a shell loop. I'm also using (master) PYTHONPROFILEIMPORTTIME=1 python3.11 -c "import uvicorn"import time: self [us] | cumulative | imported package import time: 144 | 144 | _io import time: 26 | 26 | marshal import time: 365 | 365 | posix import time: 292 | 825 | _frozen_importlib_external import time: 687 | 687 | time import time: 176 | 862 | zipimport import time: 45 | 45 | _codecs import time: 288 | 333 | codecs import time: 490 | 490 | encodings.aliases import time: 749 | 1571 | encodings import time: 195 | 195 | encodings.utf_8 import time: 63 | 63 | _signal import time: 26 | 26 | _abc import time: 98 | 124 | abc import time: 129 | 253 | io import time: 32 | 32 | _stat import time: 44 | 76 | stat import time: 642 | 642 | _collections_abc import time: 25 | 25 | genericpath import time: 41 | 66 | posixpath import time: 255 | 1037 | os import time: 172 | 172 | _sitebuiltins import time: 1147 | 1147 | _distutils_hack import time: 462 | 462 | types import time: 264 | 264 | warnings import time: 161 | 425 | importlib import time: 228 | 228 | importlib._abc import time: 86 | 86 | itertools import time: 117 | 117 | keyword import time: 57 | 57 | _operator import time: 273 | 330 | operator import time: 155 | 155 | reprlib import time: 44 | 44 | _collections import time: 832 | 1561 | collections import time: 39 | 39 | _functools import time: 449 | 487 | functools import time: 566 | 2613 | contextlib import time: 108 | 3372 | importlib.util import time: 38 | 38 | importlib.machinery import time: 1655 | 1655 | enum import time: 51 | 51 | _sre import time: 193 | 193 | re._constants import time: 260 | 453 | re._parser import time: 97 | 97 | re._casefix import time: 287 | 887 | re._compiler import time: 158 | 158 | copyreg import time: 430 | 3129 | re import time: 726 | 3855 | sitecustomize import time: 11656 | 21736 | site import time: 118 | 118 | collections.abc import time: 104 | 104 | concurrent import time: 134 | 134 | token import time: 602 | 736 | tokenize import time: 162 | 898 | linecache import time: 626 | 626 | textwrap import time: 391 | 1914 | traceback import time: 202 | 202 | _weakrefset import time: 510 | 711 | weakref import time: 23 | 23 | _string import time: 399 | 421 | string import time: 469 | 469 | threading import time: 25 | 25 | atexit import time: 1124 | 4661 | logging import time: 412 | 5073 | concurrent.futures._base import time: 123 | 5299 | concurrent.futures import time: 360 | 360 | _heapq import time: 178 | 538 | heapq import time: 602 | 602 | _socket import time: 411 | 411 | math import time: 334 | 334 | select import time: 351 | 1095 | selectors import time: 35 | 35 | errno import time: 312 | 312 | array import time: 838 | 2879 | socket import time: 36 | 36 | _locale import time: 521 | 556 | locale import time: 359 | 359 | signal import time: 275 | 275 | fcntl import time: 68 | 68 | msvcrt import time: 314 | 314 | _posixsubprocess import time: 384 | 1954 | subprocess import time: 1743 | 1743 | _ssl import time: 307 | 307 | _struct import time: 103 | 410 | struct import time: 280 | 280 | binascii import time: 184 | 873 | base64 import time: 1350 | 3964 | ssl import time: 168 | 168 | asyncio.constants import time: 728 | 728 | _ast import time: 636 | 1363 | ast import time: 275 | 275 | _opcode import time: 235 | 509 | opcode import time: 491 | 999 | dis import time: 1262 | 3624 | inspect import time: 91 | 3714 | asyncio.coroutines import time: 281 | 281 | _contextvars import time: 128 | 409 | contextvars import time: 123 | 123 | asyncio.format_helpers import time: 91 | 91 | asyncio.base_futures import time: 124 | 124 | asyncio.exceptions import time: 79 | 79 | asyncio.base_tasks import time: 406 | 699 | _asyncio import time: 356 | 1585 | asyncio.events import time: 133 | 133 | asyncio.futures import time: 161 | 161 | asyncio.protocols import time: 161 | 161 | asyncio.transports import time: 72 | 72 | asyncio.log import time: 355 | 586 | asyncio.sslproto import time: 260 | 260 | _typing import time: 1309 | 1569 | typing import time: 127 | 127 | asyncio.mixins import time: 191 | 191 | asyncio.tasks import time: 278 | 594 | asyncio.locks import time: 167 | 2329 | asyncio.staggered import time: 89 | 89 | asyncio.trsock import time: 464 | 23975 | asyncio.base_events import time: 163 | 163 | asyncio.runners import time: 134 | 134 | asyncio.queues import time: 228 | 228 | asyncio.streams import time: 114 | 114 | asyncio.subprocess import time: 84 | 84 | asyncio.taskgroups import time: 202 | 202 | asyncio.timeouts import time: 67 | 67 | asyncio.threads import time: 176 | 176 | asyncio.base_subprocess import time: 237 | 237 | asyncio.selector_events import time: 335 | 747 | asyncio.unix_events import time: 180 | 25889 | asyncio import time: 331 | 331 | _json import time: 212 | 543 | json.scanner import time: 241 | 783 | json.decoder import time: 202 | 202 | json.encoder import time: 141 | 1125 | json import time: 295 | 295 | _compat_pickle import time: 477 | 477 | _pickle import time: 69 | 69 | org import time: 8 | 77 | org.python import time: 7 | 84 | org.python.core import time: 1172 | 2027 | pickle import time: 424 | 424 | _queue import time: 293 | 716 | queue import time: 72 | 72 | org import time: 7 | 79 | org.python import time: 7 | 86 | org.python.core import time: 208 | 293 | copy import time: 359 | 3393 | logging.handlers import time: 370 | 370 | socketserver import time: 650 | 4412 | logging.config import time: 105 | 105 | fnmatch import time: 60 | 60 | _winapi import time: 56 | 56 | nt import time: 48 | 48 | nt import time: 47 | 47 | nt import time: 46 | 46 | nt import time: 45 | 45 | nt import time: 48 | 347 | ntpath import time: 86 | 86 | urllib import time: 622 | 707 | urllib.parse import time: 484 | 1642 | pathlib import time: 379 | 379 | dataclasses import time: 75 | 75 | h11._abnf import time: 193 | 193 | h11._util import time: 363 | 555 | h11._headers import time: 1655 | 2663 | h11._events import time: 142 | 142 | h11._receivebuffer import time: 261 | 261 | h11._state import time: 546 | 948 | h11._readers import time: 206 | 206 | h11._writers import time: 365 | 4181 | h11._connection import time: 66 | 66 | h11._version import time: 129 | 4375 | h11 import time: 7 | 4381 | h11._connection import time: 456 | 456 | http import time: 375 | 375 | gettext import time: 488 | 488 | _datetime import time: 470 | 958 | datetime import time: 339 | 339 | click._compat import time: 88 | 88 | click.globals import time: 253 | 340 | click.utils import time: 237 | 576 | click.exceptions import time: 382 | 2253 | click.types import time: 199 | 199 | click.parser import time: 204 | 402 | click.formatting import time: 286 | 286 | click.termui import time: 1524 | 4839 | click.core import time: 186 | 186 | click.decorators import time: 168 | 5192 | click import time: 139 | 5787 | uvicorn.logging import time: 139 | 139 | yaml.error import time: 158 | 158 | yaml.tokens import time: 165 | 165 | yaml.events import time: 80 | 80 | yaml.nodes import time: 2958 | 2958 | yaml.reader import time: 222 | 222 | yaml.scanner import time: 212 | 212 | yaml.parser import time: 86 | 86 | yaml.composer import time: 459 | 459 | yaml.constructor import time: 754 | 754 | yaml.resolver import time: 175 | 4863 | yaml.loader import time: 155 | 155 | yaml.emitter import time: 83 | 83 | yaml.serializer import time: 152 | 152 | yaml.representer import time: 120 | 509 | yaml.dumper import time: 18 | 18 | yaml.yaml import time: 435 | 453 | yaml._yaml import time: 169 | 621 | yaml.cyaml import time: 203 | 6735 | yaml import time: 78 | 78 | uvicorn.importer import time: 60 | 60 | uvicorn.middleware import time: 114 | 173 | uvicorn.middleware.asgi2 import time: 76 | 76 | uvicorn.middleware.message_logger import time: 137 | 137 | uvicorn.middleware.proxy_headers import time: 121 | 121 | uvicorn._types import time: 136 | 136 | concurrent.futures.thread import time: 111 | 366 | uvicorn.middleware.wsgi import time: 529 | 51325 | uvicorn.config import time: 866 | 866 | platform import time: 106 | 106 | email import time: 305 | 305 | _bisect import time: 83 | 388 | bisect import time: 296 | 296 | _random import time: 273 | 273 | _sha512 import time: 240 | 1196 | random import time: 330 | 330 | calendar import time: 128 | 458 | email._parseaddr import time: 71 | 71 | email.base64mime import time: 119 | 119 | email.quoprimime import time: 240 | 240 | email.errors import time: 104 | 104 | quopri import time: 67 | 170 | email.encoders import time: 111 | 708 | email.charset import time: 285 | 2751 | email.utils import time: 177 | 2927 | uvicorn.server import time: 187 | 187 | multiprocessing.process import time: 152 | 152 | multiprocessing.reduction import time: 354 | 693 | multiprocessing.context import time: 147 | 840 | multiprocessing import time: 349 | 349 | zlib import time: 168 | 168 | _compression import time: 280 | 280 | _bz2 import time: 139 | 586 | bz2 import time: 477 | 477 | _lzma import time: 131 | 607 | lzma import time: 326 | 1867 | shutil import time: 226 | 2092 | tempfile import time: 330 | 330 | _multiprocessing import time: 152 | 152 | multiprocessing.util import time: 61 | 61 | _winapi import time: 304 | 2937 | multiprocessing.connection import time: 148 | 3924 | uvicorn._subprocess import time: 194 | 4117 | uvicorn.supervisors.basereload import time: 92 | 92 | uvicorn.supervisors.multiprocess import time: 178 | 178 | watchfiles.filters import time: 88 | 88 | anyio._core import time: 366 | 453 | anyio._core._compat import time: 79 | 79 | sniffio._version import time: 78 | 78 | sniffio._impl import time: 112 | 269 | sniffio import time: 107 | 375 | anyio._core._eventloop import time: 192 | 192 | anyio._core._exceptions import time: 94 | 94 | anyio.abc._resources import time: 603 | 603 | ipaddress import time: 123 | 123 | anyio._core._typedattr import time: 106 | 106 | anyio.abc._tasks import time: 806 | 912 | anyio.abc._streams import time: 276 | 1912 | anyio.abc._sockets import time: 97 | 97 | anyio.abc._subprocesses import time: 109 | 109 | anyio.abc._testing import time: 600 | 600 | anyio.lowlevel import time: 143 | 143 | anyio._core._tasks import time: 107 | 107 | anyio._core._testing import time: 1335 | 2183 | anyio._core._synchronization import time: 249 | 249 | anyio.from_thread import time: 115 | 4755 | anyio.abc import time: 74 | 4829 | anyio.to_thread import time: 476 | 5304 | anyio._core._fileio import time: 67 | 67 | anyio._core._resources import time: 72 | 72 | anyio._core._signals import time: 55 | 55 | anyio.streams import time: 504 | 559 | anyio.streams.stapled import time: 466 | 466 | anyio.streams.tls import time: 223 | 1247 | anyio._core._sockets import time: 673 | 673 | anyio.streams.memory import time: 124 | 797 | anyio._core._streams import time: 139 | 139 | anyio._core._subprocesses import time: 185 | 8828 | anyio import time: 803 | 803 | watchfiles._rust_notify import time: 434 | 10064 | watchfiles.main import time: 198 | 198 | shlex import time: 399 | 597 | watchfiles.run import time: 69 | 69 | watchfiles.version import time: 118 | 11024 | watchfiles import time: 100 | 11124 | uvicorn.supervisors.watchfilesreload import time: 103 | 15435 | uvicorn.supervisors import time: 540 | 19767 | uvicorn.main import time: 270 | 71362 | uvicorn (4ef9150) PYTHONPROFILEIMPORTTIME=1 python3.11 -c "import uvicorn"import time: self [us] | cumulative | imported package import time: 129 | 129 | _io import time: 52 | 52 | marshal import time: 364 | 364 | posix import time: 307 | 851 | _frozen_importlib_external import time: 1042 | 1042 | time import time: 96 | 1137 | zipimport import time: 38 | 38 | _codecs import time: 281 | 318 | codecs import time: 466 | 466 | encodings.aliases import time: 661 | 1445 | encodings import time: 191 | 191 | encodings.utf_8 import time: 62 | 62 | _signal import time: 26 | 26 | _abc import time: 95 | 120 | abc import time: 130 | 249 | io import time: 31 | 31 | _stat import time: 93 | 123 | stat import time: 659 | 659 | _collections_abc import time: 23 | 23 | genericpath import time: 42 | 64 | posixpath import time: 258 | 1103 | os import time: 48 | 48 | _sitebuiltins import time: 1104 | 1104 | _distutils_hack import time: 385 | 385 | types import time: 234 | 234 | warnings import time: 162 | 395 | importlib import time: 219 | 219 | importlib._abc import time: 72 | 72 | itertools import time: 117 | 117 | keyword import time: 50 | 50 | _operator import time: 255 | 304 | operator import time: 148 | 148 | reprlib import time: 40 | 40 | _collections import time: 822 | 1501 | collections import time: 45 | 45 | _functools import time: 425 | 470 | functools import time: 515 | 2484 | contextlib import time: 89 | 3186 | importlib.util import time: 38 | 38 | importlib.machinery import time: 1795 | 1795 | enum import time: 46 | 46 | _sre import time: 196 | 196 | re._constants import time: 262 | 458 | re._parser import time: 89 | 89 | re._casefix import time: 315 | 906 | re._compiler import time: 128 | 128 | copyreg import time: 476 | 3305 | re import time: 748 | 4053 | sitecustomize import time: 11050 | 20962 | site import time: 115 | 115 | collections.abc import time: 104 | 104 | concurrent import time: 143 | 143 | token import time: 608 | 751 | tokenize import time: 173 | 924 | linecache import time: 594 | 594 | textwrap import time: 352 | 1868 | traceback import time: 159 | 159 | _weakrefset import time: 433 | 591 | weakref import time: 27 | 27 | _string import time: 494 | 520 | string import time: 522 | 522 | threading import time: 26 | 26 | atexit import time: 1121 | 4646 | logging import time: 437 | 5082 | concurrent.futures._base import time: 121 | 5306 | concurrent.futures import time: 371 | 371 | _heapq import time: 231 | 602 | heapq import time: 650 | 650 | _socket import time: 387 | 387 | math import time: 317 | 317 | select import time: 363 | 1066 | selectors import time: 30 | 30 | errno import time: 309 | 309 | array import time: 864 | 2917 | socket import time: 47 | 47 | _locale import time: 608 | 655 | locale import time: 432 | 432 | signal import time: 403 | 403 | fcntl import time: 77 | 77 | msvcrt import time: 382 | 382 | _posixsubprocess import time: 389 | 2336 | subprocess import time: 1724 | 1724 | _ssl import time: 320 | 320 | _struct import time: 109 | 428 | struct import time: 383 | 383 | binascii import time: 213 | 1024 | base64 import time: 1430 | 4176 | ssl import time: 170 | 170 | asyncio.constants import time: 654 | 654 | _ast import time: 665 | 1319 | ast import time: 286 | 286 | _opcode import time: 253 | 538 | opcode import time: 536 | 1073 | dis import time: 1398 | 3789 | inspect import time: 98 | 3886 | asyncio.coroutines import time: 298 | 298 | _contextvars import time: 138 | 436 | contextvars import time: 109 | 109 | asyncio.format_helpers import time: 115 | 115 | asyncio.base_futures import time: 138 | 138 | asyncio.exceptions import time: 85 | 85 | asyncio.base_tasks import time: 380 | 715 | _asyncio import time: 326 | 1585 | asyncio.events import time: 132 | 132 | asyncio.futures import time: 148 | 148 | asyncio.protocols import time: 153 | 153 | asyncio.transports import time: 70 | 70 | asyncio.log import time: 331 | 552 | asyncio.sslproto import time: 268 | 268 | _typing import time: 1361 | 1628 | typing import time: 134 | 134 | asyncio.mixins import time: 199 | 199 | asyncio.tasks import time: 281 | 613 | asyncio.locks import time: 169 | 2409 | asyncio.staggered import time: 89 | 89 | asyncio.trsock import time: 496 | 24914 | asyncio.base_events import time: 160 | 160 | asyncio.runners import time: 137 | 137 | asyncio.queues import time: 225 | 225 | asyncio.streams import time: 118 | 118 | asyncio.subprocess import time: 83 | 83 | asyncio.taskgroups import time: 203 | 203 | asyncio.timeouts import time: 65 | 65 | asyncio.threads import time: 178 | 178 | asyncio.base_subprocess import time: 249 | 249 | asyncio.selector_events import time: 338 | 763 | asyncio.unix_events import time: 188 | 26853 | asyncio import time: 339 | 339 | _json import time: 212 | 551 | json.scanner import time: 241 | 791 | json.decoder import time: 217 | 217 | json.encoder import time: 134 | 1141 | json import time: 206 | 206 | _compat_pickle import time: 423 | 423 | _pickle import time: 104 | 104 | org import time: 10 | 114 | org.python import time: 7 | 120 | org.python.core import time: 904 | 1652 | pickle import time: 402 | 402 | _queue import time: 323 | 724 | queue import time: 76 | 76 | org import time: 9 | 85 | org.python import time: 7 | 91 | org.python.core import time: 156 | 247 | copy import time: 381 | 3003 | logging.handlers import time: 377 | 377 | socketserver import time: 638 | 4017 | logging.config import time: 106 | 106 | fnmatch import time: 63 | 63 | _winapi import time: 58 | 58 | nt import time: 54 | 54 | nt import time: 58 | 58 | nt import time: 54 | 54 | nt import time: 51 | 51 | nt import time: 54 | 390 | ntpath import time: 95 | 95 | urllib import time: 597 | 692 | urllib.parse import time: 482 | 1669 | pathlib import time: 396 | 396 | http import time: 448 | 448 | gettext import time: 431 | 431 | _datetime import time: 463 | 894 | datetime import time: 458 | 458 | click._compat import time: 91 | 91 | click.globals import time: 218 | 309 | click.utils import time: 241 | 549 | click.exceptions import time: 396 | 2295 | click.types import time: 205 | 205 | click.parser import time: 226 | 430 | click.formatting import time: 298 | 298 | click.termui import time: 1241 | 4710 | click.core import time: 188 | 188 | click.decorators import time: 233 | 5130 | click import time: 158 | 5684 | uvicorn.logging import time: 73 | 73 | uvicorn.importer import time: 67 | 67 | uvicorn.middleware import time: 319 | 386 | uvicorn.middleware.asgi2 import time: 101 | 101 | uvicorn.middleware.message_logger import time: 130 | 130 | uvicorn.middleware.proxy_headers import time: 130 | 130 | uvicorn._types import time: 142 | 142 | concurrent.futures.thread import time: 127 | 399 | uvicorn.middleware.wsgi import time: 745 | 41192 | uvicorn.config import time: 1003 | 1003 | platform import time: 99 | 99 | email import time: 317 | 317 | _bisect import time: 99 | 416 | bisect import time: 281 | 281 | _random import time: 260 | 260 | _sha512 import time: 304 | 1260 | random import time: 280 | 280 | calendar import time: 135 | 414 | email._parseaddr import time: 63 | 63 | email.base64mime import time: 648 | 648 | email.quoprimime import time: 271 | 271 | email.errors import time: 105 | 105 | quopri import time: 77 | 182 | email.encoders import time: 116 | 1277 | email.charset import time: 293 | 3342 | email.utils import time: 181 | 3522 | uvicorn.server import time: 940 | 5464 | uvicorn.main import time: 256 | 46912 | uvicorn |
@Kludex did you have time to take a look at the changes and the way I tested it ? |
What's the improvement with only the yaml package? I like the default on |
with only the yaml package, the result is ~65ms -> ~59ms |
Ah... It's not? Then I can accept it more easily 🧐 Let me see this in a few hours when I wake up. |
Thanks 👍 |
The goal of this PR is to discuss some small improvements that could be made to improve the import time of uvicorn.
There are 3 changes here:
yaml
until we have to parse a yaml fileh11
whenhttptools
is usedOn my computer, the import time is reduced from ~60ms to ~35ms.
I don't know if the benefit is worth the changes. WDYT ?