Skip to content

perf: combined import-time optimizations (E2E reference)#10

Closed
KRRT7 wants to merge 14 commits intomasterfrom
codeflash/optimize
Closed

perf: combined import-time optimizations (E2E reference)#10
KRRT7 wants to merge 14 commits intomasterfrom
codeflash/optimize

Conversation

@KRRT7
Copy link
Copy Markdown
Owner

@KRRT7 KRRT7 commented Apr 9, 2026

E2E Results

Import master optimized Speedup
from rich.console import Console 78.1ms 52.2ms 1.50x faster
from rich.logging import RichHandler 99.4ms 56.9ms 1.75x faster
import rich 18.2ms 18.3ms (already lean)

Per-module breakdown (optimized)

Module Time
import rich 18.2ms
Console 52.5ms
RichHandler 56.4ms
Syntax 64.7ms
Traceback 92.5ms
Markdown 103.0ms

Summary

This branch stacks all import-time optimizations for E2E benchmarking. Not intended for merge — each change has its own PR for independent review:

PR Change Est. Savings
#1 Defer Traceback in logging.py ~20ms
#2 Eliminate import inspect from console.py ~10ms
#3 Remove dead logging import from segment.py ~2-3ms
#4 Defer pretty to Console.print() ~3-5ms
#5 Defer scope to Console.log() ~3-5ms
#6 Defer getpass to Console.input() ~2ms
#7 Defer configparser to Theme.from_file() ~1.5ms
#8 Replace pathlib with os.path.basename in logging.py ~4-5ms
#9 Defer html.escape and zlib to export methods ~2.3ms
#11 from __future__ import annotations + TYPE_CHECKING in logging.py ~6ms
Remove dead _svg_hash function (fixes latent NameError) cleanup
Move console imports to TYPE_CHECKING in syntax.py eliminates console.py from syntax import chain
Replace inspect.isclass with isinstance(x, type) in protocol.py prepares for dataclasses removal
Defer import inspect in repr.py prepares for dataclasses removal

Benchmarks

Environment: Standard_D2s_v5 (non-burstable), CPython 3.13.13, hyperfine --min-runs 30

hyperfine -L branch master,codeflash/optimize \
  --setup 'git checkout {branch} && pip install -e . -q' \
  -n '{branch}' \
  'python -c "from rich.console import Console"'

Testing

952 passed, 25 skipped. Verified with make typecheck (mypy) and make format-check (black).

Disclosure

Developed with AI assistance (codeflash-agent). Benchmarked in a controlled environment (dedicated non-burstable Azure VM). All tests verified across multiple Python implementations.

KRRT7 added 11 commits April 8, 2026 23:31
…rt time

Move the module-level `from .traceback import Traceback` to a lazy
import inside `emit()`, guarded by the `rich_tracebacks` check. This
avoids loading the traceback -> syntax -> pygments chain (~20ms) when
RichHandler is used without rich tracebacks enabled (the default).

The Traceback type annotation in `render()` is changed to a string
literal for forward-reference compatibility.
Replace `import inspect` + `from inspect import isclass` with:
- `isinstance(obj, type)` for isclass (identical behavior)
- `sys._getframe` for inspect.currentframe (same underlying call)
- Lazy `from inspect import stack` in the fallback path only

inspect pulls in dis, tokenize, ast, and annotationlib (~10ms).
This eliminates that cost from Console's import path entirely.
`getLogger("rich")` was imported and assigned to `log` but never used
anywhere in segment.py. Removing it avoids importing the `logging`
module (and its transitive chain: traceback, linecache, tokenize)
on the Console import path.
Move `from .pretty import Pretty, is_expandable` from module level
to inside the `_collect_renderables()` method (called by `print()`).

rich.pretty pulls in dataclasses, reprlib, array, and tries `import
attr` — none of which are needed until something is actually printed.
Move `from .scope import render_scope` from module level to inside
the `log()` method, gated behind the `log_locals` check.

rich.scope pulls in rich.table, rich.panel, and fractions — none of
which are needed unless log_locals=True is explicitly requested.
Move `from getpass import getpass` from module level to inside the
`input()` method, only when password=True. The getpass module imports
contextlib and termios which are unnecessary for normal Console usage.
Move `import configparser` from module level to inside from_file().
configparser is only needed when loading themes from .ini files,
not during normal Console/Theme construction.
Replace `from pathlib import Path` + `Path(x).name` with
`os.path.basename(x)`. os is already imported transitively and
available at near-zero cost, while pathlib adds ~4-5ms to the
RichHandler import path.

This is also a minor runtime improvement: os.path.basename is a
simple string operation vs Path object construction + attribute access.
Move `from html import escape` and `import zlib` from module level
to inside export_html() and export_svg() respectively. These modules
are only needed for HTML/SVG export operations, not for normal
Console usage.
Run `python benchmarks/bench_import.py` to measure RichHandler import
time and see the Console-to-RichHandler gap that this PR reduces.
Replace two separate hyperfine invocations with a single parameterized
run using -L branch. This produces a proper relative comparison with
statistical significance in one invocation.

Also adds a Console baseline measurement to show the optimization
only affects the traceback import chain, not the common path.
KRRT7 added 3 commits April 9, 2026 00:08
Add `from __future__ import annotations` and move Console,
ConsoleRenderable, Highlighter, FormatTimeCallable, and Traceback
to TYPE_CHECKING blocks. These are all annotation-only — none are
used at runtime.

This avoids importing rich.console (~8ms) entirely when importing
RichHandler, since _log_render, text, and highlighter don't import
console at module level either.
_svg_hash has been unused since the SVG export rewrite in 113997a
(May 2022). The only caller (classes_prefix namespacing) was removed
but the function definition was left behind. Removing it also fixes
a latent NameError since zlib was deferred to export_svg() in the
previous commit.
- syntax.py: move Console, ConsoleOptions, JustifyMethod, RenderResult
  to TYPE_CHECKING (already has future annotations). Eliminates
  rich.console from syntax's import chain entirely.
- protocol.py: replace `from inspect import isclass` with
  `isinstance(x, type)` — identical behavior, no import needed.
- repr.py: defer `import inspect` into auto_rich_repr() where
  inspect.signature() is actually used.

None of these save time yet (inspect still comes in via dataclasses),
but they become free wins once dataclasses is removed from console.py.
@KRRT7
Copy link
Copy Markdown
Owner Author

KRRT7 commented Apr 9, 2026

Superseded by the consolidated stacked PRs #12 + #13.

@KRRT7 KRRT7 closed this Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant