Skip to content

Commit 41f14f7

Browse files
Optimize ExpectCallTransformer.transform
The optimized code achieves a **172% speedup** (85.2ms → 31.3ms) by eliminating a critical O(n²) performance bottleneck in the `transform()` method. ## Key Optimization **Problem**: The original code called `is_inside_string(code, match.start())` for every regex match found. This function scans from position 0 to the match position each time, resulting in O(n²) complexity when processing code with many matches. **Solution**: The optimization replaces these repeated scans with **incremental string state tracking** directly in the main loop. Instead of rescanning from the beginning for each match, the code maintains `in_string`, `string_char`, and `last_checked_pos` variables that preserve state between iterations. When a new match is found, only the code between `last_checked_pos` and `match_start` is scanned to update the string state. ## Performance Impact The line profiler data clearly shows the improvement: - **Original**: `is_inside_string()` consumed 0.618s (95.2% of transform time) with 432 calls scanning 887,510 characters total - **Optimized**: The inline tracking logic in transform() consumes only 0.021s (33% of transform time) by scanning just 28,273 characters incrementally Test results demonstrate strong gains on workloads with many matches: - `test_transform_many_invocations`: 12.3ms → 4.84ms (155% faster) - `test_transform_large_code_file`: 40.6ms → 14.0ms (191% faster) - `test_transform_alternating_patterns`: 2.64ms → 519μs (408% faster) - `test_transform_mixed_qualified_names`: 14.0ms → 5.14ms (173% faster) The optimization particularly benefits code with frequent expect() calls, as each avoided `is_inside_string()` call saves scanning hundreds or thousands of characters. This makes the transformer significantly faster on realistic test files with multiple assertions.
1 parent a3ca8ee commit 41f14f7

1 file changed

Lines changed: 29 additions & 1 deletion

File tree

codeflash/languages/javascript/instrument.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,10 @@ def transform(self, code: str) -> str:
603603
"""Transform all expect calls in the code."""
604604
result: list[str] = []
605605
pos = 0
606+
# Track string state incrementally to avoid O(n²) rescanning
607+
in_string = False
608+
string_char = None
609+
last_checked_pos = 0
606610

607611
while pos < len(code):
608612
expect_match = self._expect_pattern.search(code, pos)
@@ -627,8 +631,32 @@ def transform(self, code: str) -> str:
627631
result.append(code[pos:])
628632
break
629633

634+
# Update string state up to match.start() incrementally
635+
match_start = match.start()
636+
i = last_checked_pos
637+
while i < match_start:
638+
char = code[i]
639+
640+
if in_string:
641+
# Check for escape sequence
642+
if char == "\\" and i + 1 < len(code):
643+
i += 2
644+
continue
645+
# Check for end of string
646+
if char == string_char:
647+
in_string = False
648+
string_char = None
649+
# Check for start of string
650+
elif char in "\"'`":
651+
in_string = True
652+
string_char = char
653+
654+
i += 1
655+
656+
last_checked_pos = match_start
657+
630658
# Skip if inside a string literal (e.g., test description)
631-
if is_inside_string(code, match.start()):
659+
if in_string:
632660
result.append(code[pos : match.end()])
633661
pos = match.end()
634662
continue

0 commit comments

Comments
 (0)