Skip to content

Commit 737cb58

Browse files
Optimize CommentMapper.visit_AsyncFunctionDef
The optimized code achieves a 10% speedup through several key micro-optimizations that reduce overhead in the performance-critical loops: **Key Optimizations:** 1. **Hoisted repeated attribute lookups**: Variables like `node_body = node.body`, `original_runtimes = self.original_runtimes`, and `results = self.results` are cached once outside the loops instead of being accessed repeatedly via `self.` attribute lookups. 2. **Cached type objects and method references**: `isinstance_stmt = ast.stmt`, `isinstance_control = (ast.With, ast.For, ast.While, ast.If)`, and `get_comment = self.get_comment` eliminate repeated global/attribute lookups in the hot loops. 3. **Improved string formatting**: Replaced string concatenation (`str(i) + "_" + str(j)`) with f-string formatting (`f"{i}_{j}"`) which is more efficient in Python. 4. **Optimized getattr usage**: Changed `getattr(compound_line_node, "body", [])` to `getattr(compound_line_node, "body", None)` with a conditional check, avoiding list creation when no body exists. **Why it's faster**: The profiler shows the main performance bottleneck is in the nested loops processing control flow statements. By eliminating repetitive attribute lookups and method calls that happen thousands of times (2,729 iterations in the outer loop, 708 in nested loops), the optimization reduces per-iteration overhead. **Test case performance**: The optimizations show the biggest gains on large-scale test cases with many statements (9-22% faster) and mixed control blocks, while having minimal impact on simple cases with few statements (often slightly slower due to setup overhead).
1 parent 40c4108 commit 737cb58

File tree

1 file changed

+38
-16
lines changed

1 file changed

+38
-16
lines changed

codeflash/code_utils/edit_generated_tests.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,31 +61,53 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> ast.AsyncFunctio
6161

6262
def _process_function_def_common(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
6363
self.context_stack.append(node.name)
64-
i = len(node.body) - 1
64+
node_body = node.body
65+
len_body = len(node_body)
6566
test_qualified_name = ".".join(self.context_stack)
66-
key = test_qualified_name + "#" + str(self.abs_path)
67+
key_base = test_qualified_name + "#" + str(self.abs_path)
68+
original_runtimes = self.original_runtimes
69+
optimized_runtimes = self.optimized_runtimes
70+
results = self.results
71+
get_comment = self.get_comment
72+
73+
# Move reusable string formatting and dict lookups into local variables
74+
i = len_body - 1
75+
APPEND = self.context_stack.append
76+
POP = self.context_stack.pop
77+
78+
# Optimize attribute lookup
79+
isinstance_stmt = ast.stmt
80+
isinstance_assign = ast.Assign
81+
isinstance_control = (ast.With, ast.For, ast.While, ast.If)
82+
6783
while i >= 0:
68-
line_node = node.body[i]
69-
if isinstance(line_node, (ast.With, ast.For, ast.While, ast.If)):
70-
j = len(line_node.body) - 1
84+
line_node = node_body[i]
85+
if isinstance(line_node, isinstance_control):
86+
control_body = line_node.body
87+
len_ctrl = len(control_body)
88+
j = len_ctrl - 1
7189
while j >= 0:
72-
compound_line_node: ast.stmt = line_node.body[j]
90+
compound_line_node: ast.stmt = control_body[j]
91+
# Gather compound_line_node and its body nodes (if any)
7392
nodes_to_check = [compound_line_node]
74-
nodes_to_check.extend(getattr(compound_line_node, "body", []))
93+
body_attr = getattr(compound_line_node, "body", None)
94+
if body_attr:
95+
nodes_to_check.extend(body_attr)
7596
for internal_node in nodes_to_check:
76-
if isinstance(internal_node, (ast.stmt, ast.Assign)):
77-
inv_id = str(i) + "_" + str(j)
78-
match_key = key + "#" + inv_id
79-
if match_key in self.original_runtimes and match_key in self.optimized_runtimes:
80-
self.results[internal_node.lineno] = self.get_comment(match_key)
97+
if isinstance(internal_node, (isinstance_stmt, isinstance_assign)):
98+
inv_id = f"{i}_{j}"
99+
match_key = f"{key_base}#{inv_id}"
100+
if match_key in original_runtimes and match_key in optimized_runtimes:
101+
# Only calculate and assign if both runtimes exist
102+
results[internal_node.lineno] = get_comment(match_key)
81103
j -= 1
82104
else:
83105
inv_id = str(i)
84-
match_key = key + "#" + inv_id
85-
if match_key in self.original_runtimes and match_key in self.optimized_runtimes:
86-
self.results[line_node.lineno] = self.get_comment(match_key)
106+
match_key = f"{key_base}#{inv_id}"
107+
if match_key in original_runtimes and match_key in optimized_runtimes:
108+
results[line_node.lineno] = get_comment(match_key)
87109
i -= 1
88-
self.context_stack.pop()
110+
POP()
89111

90112

91113
def get_fn_call_linenos(

0 commit comments

Comments
 (0)