Skip to content

Commit b533f50

Browse files
committed
perf: backport libcst visitor dispatch cache from codeflash-python
Cache the visitor dispatch tables that libcst rebuilds on every MatcherDecoratableTransformer/Visitor instantiation. The tables depend only on the class, not the instance, so caching by type is safe. Saves ~27ms per visitor instantiation (24x faster). Also fix pre-existing ruff F821 in cli.py (missing exit_with_message import in process_pyproject_config).
1 parent 61053be commit b533f50

13 files changed

Lines changed: 78 additions & 1 deletion

codeflash/benchmarking/instrument_codeflash_trace.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import libcst as cst
66

7+
import codeflash.code_utils._libcst_cache # noqa: F401
78
from codeflash.code_utils.formatter import sort_imports
89

910
if TYPE_CHECKING:

codeflash/cli_cmds/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def process_and_validate_cmd_args(args: Namespace) -> Namespace:
8282

8383
def process_pyproject_config(args: Namespace) -> Namespace:
8484
from codeflash.code_utils import env_utils
85-
from codeflash.code_utils.code_utils import normalize_ignore_paths
85+
from codeflash.code_utils.code_utils import exit_with_message, normalize_ignore_paths
8686
from codeflash.code_utils.config_parser import parse_config_file
8787
from codeflash.languages.test_framework import set_current_test_framework
8888
from codeflash.lsp.helpers import is_LSP_enabled
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""Cache libcst visitor dispatch table construction.
2+
3+
libcst's ``MatcherDecoratableTransformer`` and
4+
``MatcherDecoratableVisitor`` rebuild visitor dispatch tables on
5+
every instantiation by iterating ``dir(self)`` (~600 attributes)
6+
and calling ``getattr`` + ``inspect.ismethod`` on each. The
7+
results depend only on the class, not the instance, so caching
8+
by ``type(obj)`` is safe.
9+
10+
Import this module before any libcst visitors are instantiated
11+
to install the cache.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
from typing import Any
17+
18+
import libcst.matchers._visitors as _mv
19+
20+
_visit_cache: dict[type, Any] = {}
21+
_leave_cache: dict[type, Any] = {}
22+
_matchers_cache: dict[type, Any] = {}
23+
24+
_original_visit = _mv._gather_constructed_visit_funcs # noqa: SLF001
25+
_original_leave = _mv._gather_constructed_leave_funcs # noqa: SLF001
26+
_original_matchers = _mv._gather_matchers # noqa: SLF001
27+
28+
29+
def _cached_visit(obj: object) -> Any:
30+
"""Return cached visit-function dispatch table for the object's class."""
31+
cls = type(obj)
32+
try:
33+
return _visit_cache[cls]
34+
except KeyError:
35+
result = _original_visit(obj)
36+
_visit_cache[cls] = result
37+
return result
38+
39+
40+
def _cached_leave(obj: object) -> Any:
41+
"""Return cached leave-function dispatch table for the object's class."""
42+
cls = type(obj)
43+
try:
44+
return _leave_cache[cls]
45+
except KeyError:
46+
result = _original_leave(obj)
47+
_leave_cache[cls] = result
48+
return result
49+
50+
51+
def _cached_matchers(obj: object) -> Any:
52+
"""Return cached matcher dispatch table for the object's class."""
53+
cls = type(obj)
54+
try:
55+
return dict(_matchers_cache[cls])
56+
except KeyError:
57+
result = _original_matchers(obj)
58+
_matchers_cache[cls] = result
59+
return dict(result)
60+
61+
62+
_mv._gather_constructed_visit_funcs = _cached_visit # noqa: SLF001
63+
_mv._gather_constructed_leave_funcs = _cached_leave # noqa: SLF001
64+
_mv._gather_matchers = _cached_matchers # noqa: SLF001

codeflash/code_utils/instrument_existing_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import libcst as cst
99

10+
import codeflash.code_utils._libcst_cache # noqa: F401
1011
from codeflash.cli_cmds.console import logger
1112
from codeflash.code_utils.code_utils import get_run_tmp_file, module_name_from_file_path
1213
from codeflash.code_utils.formatter import sort_imports

codeflash/languages/function_optimizer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from rich.text import Text
2121
from rich.tree import Tree
2222

23+
import codeflash.code_utils._libcst_cache # noqa: F401
2324
from codeflash.api.aiservice import AiServiceClient, AIServiceRefinerRequest, LocalAiServiceClient
2425
from codeflash.api.cfapi import add_code_context_hash, create_staging, get_cfapi_base_urls, mark_optimization_success
2526
from codeflash.benchmarking.utils import process_benchmark_data

codeflash/languages/python/context/code_context_extractor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import libcst as cst
1313

14+
import codeflash.code_utils._libcst_cache # noqa: F401
1415
from codeflash.cli_cmds.console import logger
1516
from codeflash.code_utils.code_utils import encoded_tokens_len, get_qualified_name, path_belongs_to_site_packages
1617
from codeflash.code_utils.config_consts import (

codeflash/languages/python/context/unused_definition_remover.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import libcst as cst
1111

12+
import codeflash.code_utils._libcst_cache # noqa: F401
1213
from codeflash.cli_cmds.console import logger
1314
from codeflash.languages import current_language
1415
from codeflash.languages.base import Language

codeflash/languages/python/static_analysis/code_extractor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from libcst.codemod.visitors import AddImportsVisitor, GatherImportsVisitor, RemoveImportsVisitor
1212
from libcst.helpers import calculate_module_and_package
1313

14+
import codeflash.code_utils._libcst_cache # noqa: F401
1415
from codeflash.cli_cmds.console import logger
1516
from codeflash.code_utils.config_consts import MAX_CONTEXT_LEN_REVIEW
1617
from codeflash.languages.base import Language

codeflash/languages/python/static_analysis/code_replacer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import libcst as cst
1010
from libcst.metadata import PositionProvider
1111

12+
import codeflash.code_utils._libcst_cache # noqa: F401
1213
from codeflash.cli_cmds.console import logger
1314
from codeflash.code_utils.config_parser import find_conftest_files
1415
from codeflash.code_utils.formatter import sort_imports

codeflash/languages/python/static_analysis/edit_generated_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from libcst import MetadataWrapper
1111
from libcst.metadata import PositionProvider
1212

13+
import codeflash.code_utils._libcst_cache # noqa: F401
1314
from codeflash.cli_cmds.console import logger
1415
from codeflash.code_utils.time_utils import format_perf, format_time
1516
from codeflash.models.models import GeneratedTests, GeneratedTestsList

0 commit comments

Comments
 (0)