Skip to content

Commit 1a87de7

Browse files
authored
bpo-31415: Add -X importtime option (GH-3490)
It shows show import time of each module. It's useful for optimizing startup time. Typical usage: python -X importtime -c 'import requests'
1 parent e8c368d commit 1a87de7

File tree

3 files changed

+51
-0
lines changed

3 files changed

+51
-0
lines changed

Doc/using/cmdline.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,10 @@ Miscellaneous options
407407
* ``-X showalloccount`` to output the total count of allocated objects for
408408
each type when the program finishes. This only works when Python was built with
409409
``COUNT_ALLOCS`` defined.
410+
* ``-X importtime`` to show how long each import takes. It shows module name,
411+
cumulative time (including nested imports) and self time (exluding nested
412+
imports). Note that its output may be broken in multi threaded application.
413+
Typical usage is ``python3 -X importtime -c 'import asyncio'``.
410414

411415
It also allows passing arbitrary values and retrieving them through the
412416
:data:`sys._xoptions` dictionary.
@@ -423,6 +427,9 @@ Miscellaneous options
423427
.. versionadded:: 3.6
424428
The ``-X showalloccount`` option.
425429

430+
.. versionadded:: 3.7
431+
The ``-X importtime`` option.
432+
426433

427434
Options you shouldn't use
428435
~~~~~~~~~~~~~~~~~~~~~~~~~
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add ``-X importtime`` option to show how long each import takes. It can
2+
be used to optimize application's startup time.

Python/import.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,8 +1667,38 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
16671667
}
16681668
}
16691669
else {
1670+
static int ximporttime = 0;
1671+
static int import_level;
1672+
static _PyTime_t accumulated;
1673+
_Py_IDENTIFIER(importtime);
1674+
1675+
_PyTime_t t1 = 0, accumulated_copy = accumulated;
1676+
16701677
Py_XDECREF(mod);
16711678

1679+
/* XOptions is initialized after first some imports.
1680+
* So we can't have negative cache.
1681+
* Anyway, importlib.__find_and_load is much slower than
1682+
* _PyDict_GetItemId()
1683+
*/
1684+
if (ximporttime == 0) {
1685+
PyObject *xoptions = PySys_GetXOptions();
1686+
if (xoptions) {
1687+
PyObject *value = _PyDict_GetItemId(xoptions, &PyId_importtime);
1688+
ximporttime = (value == Py_True);
1689+
}
1690+
if (ximporttime) {
1691+
fputs("import time: self [us] | cumulative | imported package\n",
1692+
stderr);
1693+
}
1694+
}
1695+
1696+
if (ximporttime) {
1697+
import_level++;
1698+
t1 = _PyTime_GetMonotonicClock();
1699+
accumulated = 0;
1700+
}
1701+
16721702
if (PyDTrace_IMPORT_FIND_LOAD_START_ENABLED())
16731703
PyDTrace_IMPORT_FIND_LOAD_START(PyUnicode_AsUTF8(abs_name));
16741704

@@ -1680,6 +1710,18 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
16801710
PyDTrace_IMPORT_FIND_LOAD_DONE(PyUnicode_AsUTF8(abs_name),
16811711
mod != NULL);
16821712

1713+
if (ximporttime) {
1714+
_PyTime_t cum = _PyTime_GetMonotonicClock() - t1;
1715+
1716+
import_level--;
1717+
fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n",
1718+
(long)_PyTime_AsMicroseconds(cum - accumulated, _PyTime_ROUND_CEILING),
1719+
(long)_PyTime_AsMicroseconds(cum, _PyTime_ROUND_CEILING),
1720+
import_level*2, "", PyUnicode_AsUTF8(abs_name));
1721+
1722+
accumulated = accumulated_copy + cum;
1723+
}
1724+
16831725
if (mod == NULL) {
16841726
goto error;
16851727
}

0 commit comments

Comments
 (0)