Skip to content

Commit 91870c0

Browse files
authored
Merge pull request #784 from codeflash-ai/codeflash/optimize-pr363-2025-09-30T01.44.19
⚡️ Speed up method `InitDecorator.visit_ClassDef` by 65% in PR #363 (`part-1-windows-fixes`)
2 parents 824d8f6 + 0f51ff7 commit 91870c0

File tree

1 file changed

+37
-26
lines changed

1 file changed

+37
-26
lines changed

codeflash/verification/instrument_codeflash_capture.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,27 @@ def __init__(
9292
self.tests_root = tests_root
9393
self.inserted_decorator = False
9494

95+
# Precompute decorator components to avoid reconstructing on every node visit
96+
# Only the `function_name` field changes per class
97+
self._base_decorator_keywords = [
98+
ast.keyword(arg="tmp_dir_path", value=ast.Constant(value=self.tmp_dir_path)),
99+
ast.keyword(arg="tests_root", value=ast.Constant(value=self.tests_root.as_posix())),
100+
ast.keyword(arg="is_fto", value=ast.Constant(value=self.is_fto)),
101+
]
102+
self._base_decorator_func = ast.Name(id="codeflash_capture", ctx=ast.Load())
103+
104+
# Preconstruct starred/kwargs for super init injection for perf
105+
self._super_starred = ast.Starred(value=ast.Name(id="args", ctx=ast.Load()))
106+
self._super_kwarg = ast.keyword(arg=None, value=ast.Name(id="kwargs", ctx=ast.Load()))
107+
self._super_func = ast.Attribute(
108+
value=ast.Call(func=ast.Name(id="super", ctx=ast.Load()), args=[], keywords=[]),
109+
attr="__init__",
110+
ctx=ast.Load(),
111+
)
112+
self._init_vararg = ast.arg(arg="args")
113+
self._init_kwarg = ast.arg(arg="kwargs")
114+
self._init_self_arg = ast.arg(arg="self", annotation=None)
115+
95116
def visit_ImportFrom(self, node: ast.ImportFrom) -> ast.ImportFrom:
96117
# Check if our import already exists
97118
if node.module == "codeflash.verification.codeflash_capture" and any(
@@ -114,21 +135,18 @@ def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef:
114135
if node.name not in self.target_classes:
115136
return node
116137

117-
# Look for __init__ method
118138
has_init = False
119-
120-
# Create the decorator
139+
# Build decorator node ONCE for each class, not per loop iteration
121140
decorator = ast.Call(
122-
func=ast.Name(id="codeflash_capture", ctx=ast.Load()),
141+
func=self._base_decorator_func,
123142
args=[],
124143
keywords=[
125144
ast.keyword(arg="function_name", value=ast.Constant(value=f"{node.name}.__init__")),
126-
ast.keyword(arg="tmp_dir_path", value=ast.Constant(value=self.tmp_dir_path)),
127-
ast.keyword(arg="tests_root", value=ast.Constant(value=self.tests_root.as_posix())),
128-
ast.keyword(arg="is_fto", value=ast.Constant(value=self.is_fto)),
145+
*self._base_decorator_keywords,
129146
],
130147
)
131148

149+
# Only scan node.body once for both __init__ and decorator check
132150
for item in node.body:
133151
if (
134152
isinstance(item, ast.FunctionDef)
@@ -139,35 +157,28 @@ def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef:
139157
):
140158
has_init = True
141159

142-
# Add decorator at the start of the list if not already present
143-
if not any(
144-
isinstance(d, ast.Call) and isinstance(d.func, ast.Name) and d.func.id == "codeflash_capture"
145-
for d in item.decorator_list
146-
):
160+
# Check for existing decorator in-place, stop after finding one
161+
for d in item.decorator_list:
162+
if isinstance(d, ast.Call) and isinstance(d.func, ast.Name) and d.func.id == "codeflash_capture":
163+
break
164+
else:
165+
# No decorator found
147166
item.decorator_list.insert(0, decorator)
148167
self.inserted_decorator = True
149168

150169
if not has_init:
151-
# Create super().__init__(*args, **kwargs) call
170+
# Create super().__init__(*args, **kwargs) call (use prebuilt AST fragments)
152171
super_call = ast.Expr(
153-
value=ast.Call(
154-
func=ast.Attribute(
155-
value=ast.Call(func=ast.Name(id="super", ctx=ast.Load()), args=[], keywords=[]),
156-
attr="__init__",
157-
ctx=ast.Load(),
158-
),
159-
args=[ast.Starred(value=ast.Name(id="args", ctx=ast.Load()))],
160-
keywords=[ast.keyword(arg=None, value=ast.Name(id="kwargs", ctx=ast.Load()))],
161-
)
172+
value=ast.Call(func=self._super_func, args=[self._super_starred], keywords=[self._super_kwarg])
162173
)
163-
# Create function arguments: self, *args, **kwargs
174+
# Create function arguments: self, *args, **kwargs (reuse arg nodes)
164175
arguments = ast.arguments(
165176
posonlyargs=[],
166-
args=[ast.arg(arg="self", annotation=None)],
167-
vararg=ast.arg(arg="args"),
177+
args=[self._init_self_arg],
178+
vararg=self._init_vararg,
168179
kwonlyargs=[],
169180
kw_defaults=[],
170-
kwarg=ast.arg(arg="kwargs"),
181+
kwarg=self._init_kwarg,
171182
defaults=[],
172183
)
173184

0 commit comments

Comments
 (0)