Skip to content

Commit 4c4feef

Browse files
Optimize CommentMapper.visit_FunctionDef
The optimized code achieves an 11% speedup through several targeted micro-optimizations that reduce attribute lookups and function call overhead in the hot path: **Key Optimizations:** 1. **Local Variable Caching**: Frequently accessed attributes (`self.context_stack`, `self.original_runtimes`, etc.) are stored in local variables at function start, eliminating repeated `self.` lookups in tight loops. 2. **Method Reference Caching**: `self.get_comment` is cached as `get_comment`, and `context_stack.append`/`pop` are stored locally, reducing method lookup overhead in the main processing loop. 3. **Type Tuple Pre-computation**: The commonly used type tuples for `isinstance` checks are stored in local variables (`_stmt_types`, `_node_stmt_assign`), avoiding tuple creation on every iteration. 4. **Optimized Node Collection**: The inefficient pattern of creating a list then extending it (`nodes_to_check = [...]; nodes_to_check.extend(...)`) is replaced with conditional unpacking (`[compound_line_node, *body_attr]` or `[compound_line_node]`), reducing list operations. 5. **f-string Usage**: String concatenations for `inv_id` and `match_key` are converted to f-strings, which are faster than concatenation operations. **Performance Characteristics:** - Best gains on **large-scale test cases** (24.9-32.5% faster) with many nested blocks or statements, where the micro-optimizations compound - Minimal overhead on **simple cases** (0.6-7.9% variance), showing the optimizations don't hurt baseline performance - Most effective when processing complex ASTs with deep nesting, as seen in the `test_large_many_nested_blocks` (24.9% faster) and `test_large_sparse_runtime_keys` (32.5% faster) cases The optimizations target the innermost loops where attribute lookups and object creation happen most frequently, making them particularly effective for batch AST processing workflows.
1 parent 40c4108 commit 4c4feef

File tree

1 file changed

+46
-20
lines changed

1 file changed

+46
-20
lines changed

codeflash/code_utils/edit_generated_tests.py

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,58 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> ast.AsyncFunctio
6060
return node
6161

6262
def _process_function_def_common(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
63-
self.context_stack.append(node.name)
64-
i = len(node.body) - 1
65-
test_qualified_name = ".".join(self.context_stack)
66-
key = test_qualified_name + "#" + str(self.abs_path)
63+
context_stack = self.context_stack
64+
original_runtimes = self.original_runtimes
65+
optimized_runtimes = self.optimized_runtimes
66+
results = self.results
67+
abs_path = self.abs_path
68+
69+
append_context = context_stack.append
70+
pop_context = context_stack.pop
71+
72+
append_context(node.name)
73+
node_body = node.body
74+
node_body_len = len(node_body)
75+
test_qualified_name = ".".join(context_stack)
76+
key = test_qualified_name + "#" + str(abs_path)
77+
78+
# Pull method locally for faster access
79+
get_comment = self.get_comment
80+
81+
# Pre-fetch 'isinstance' and types for a tiny speedup in the inner loop
82+
_stmt_types = (ast.With, ast.For, ast.While, ast.If)
83+
_node_stmt_assign = (ast.stmt, ast.Assign)
84+
85+
i = node_body_len - 1
6786
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
87+
line_node = node_body[i]
88+
if isinstance(line_node, _stmt_types):
89+
sub_body = line_node.body
90+
j = len(sub_body) - 1
7191
while j >= 0:
72-
compound_line_node: ast.stmt = line_node.body[j]
73-
nodes_to_check = [compound_line_node]
74-
nodes_to_check.extend(getattr(compound_line_node, "body", []))
92+
compound_line_node: ast.stmt = sub_body[j]
93+
# Compose nodes to check, minimizing overhead
94+
body_attr = getattr(compound_line_node, "body", None)
95+
if body_attr:
96+
# Since most ast statement bodies are lists, perform one extend
97+
nodes_to_check = [compound_line_node, *body_attr]
98+
else:
99+
nodes_to_check = [compound_line_node]
75100
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)
101+
# isinstance(internal_node, ast.Assign) is always True if isinstance(internal_node, ast.stmt)
102+
if isinstance(internal_node, _node_stmt_assign):
103+
inv_id = f"{i}_{j}"
104+
match_key = f"{key}#{inv_id}"
105+
if match_key in original_runtimes and match_key in optimized_runtimes:
106+
results[internal_node.lineno] = get_comment(match_key)
81107
j -= 1
82108
else:
83-
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)
109+
inv_id = f"{i}"
110+
match_key = f"{key}#{inv_id}"
111+
if match_key in original_runtimes and match_key in optimized_runtimes:
112+
results[line_node.lineno] = get_comment(match_key)
87113
i -= 1
88-
self.context_stack.pop()
114+
pop_context()
89115

90116

91117
def get_fn_call_linenos(

0 commit comments

Comments
 (0)