Skip to content

Commit ce1f8da

Browse files
Optimize get_optimized_code_for_module
Runtime improvement (primary): the optimized version reduces end-to-end runtime from 48.6 ms to 43.5 ms — an 11% overall speedup. Many hot-call scenarios (repeated lookups, large mappings and bulk iterations) see much larger per-case gains (up to ~80% in repeated calls and ~50% on some large-map lookups in the annotated tests). What changed (concrete optimizations) - CodeStringsMarkdown.file_to_path: - Replaced a two-step .get(...) / indexing pattern with a single try/except KeyError around self._cache["file_to_path"]. This avoids multiple dict lookups and branches when the cache exists. - Builds and caches the mapping only on the KeyError path (so successful fast-path returns are a single dict access). - get_optimized_code_for_module: - Compute str(relative_path) once (str_relative) and reuse it instead of calling str(...) repeatedly. - Avoid constructing full lists of keys and Path objects when searching for similar filenames: - Iterate file_to_code_context keys directly (no temporary available_files list unless needed). - Use os.path.basename(f) instead of Path(f).name to avoid allocating Path objects; os.path.basename is a thin C-level operation and much cheaper for simple basename extraction. - Defer construction of available_files (list(file_to_code_context.keys())) until actually needed for logging, avoiding unnecessary allocations in the common case. Why this speeds things up (technical reasons) - Less Python-level work and fewer allocations: the original code performed more dict lookups, created temporary lists, and built many Path objects inside a list comprehension — each Path(...) allocates a Python object and calls methods, which is expensive in hot loops. The optimized code reduces object construction and reduces interpreter-level branching. - Fewer lookups: switching to try/except for the cached value reduces the number of dictionary key operations on the hot path (successful cache hit path becomes a single access). - Cheaper basename extraction: os.path.basename is implemented in C and avoids constructing heavy Path objects for each candidate, which lowers per-iteration overhead when scanning many keys. - Deferred work: only produce heavy values (available_files list) when we actually need them for a warning/debug path, so the common successful-case remains minimal. How this affects existing workloads (based on tests and likely hot paths) - Big wins when the function is called many times or the mapping is large: - Repeated calls to the same path (hot path) benefit heavily because file_to_path cache access and the simple get(...) are cheap. - Large mappings where we occasionally scan keys for similarity gain because we avoid Path allocations and unnecessary list construction. - Minimal/zero impact for simple single-shot calls where no scanning occurs beyond the direct dict get. - A few tests show micro-regressions (~0–2% slower in isolated cases). These are tiny and reasonable trade-offs for the improved aggregate runtime and much larger wins on hot workloads — e.g., a single extra function call or slightly different branching can explain sub-percent differences. Behavioral/key-dependency notes - Semantics preserved: fallback logic, similarity detection and logging behavior remain functionally the same. The only behavioral change is internal ordering of checks and how we detect basenames; that produces equivalent results for path strings. - New import of os is local and trivial; no new external dependencies. Which test cases benefit most (from annotated_tests) - Repeated-calls and large-map iteration tests show the largest improvements (repeated_calls_use_cached_file_to_path, large_mapping_retrieve_multiple_entries, and the large-map loop). - Tests that exercise the “scan for similar filename” logic also improve because os.path.basename avoids Path allocations across many keys (large_scale_many_entries_similar_filenames_detected_among_many). - A few single-call tests show negligible change or very small regressions, which is an acceptable trade-off given the substantial wins on hot paths. Summary - Primary win: 11% overall runtime reduction (with much larger wins on hot paths). - How: reduce dict lookups, avoid temporary lists, eliminate Path(...) allocations in tight loops, reuse computed strings, and defer expensive work. - Trade-offs: minor micro-regressions in a couple of edge micro-benchmarks, but these are acceptable given the improved throughput and much larger gains where it matters (repeated and large-scale calls).
1 parent ea66451 commit ce1f8da

2 files changed

Lines changed: 17 additions & 9 deletions

File tree

codeflash/languages/python/static_analysis/code_replacer.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
)
2222
from codeflash.languages.python.static_analysis.line_profile_utils import ImportAdder
2323
from codeflash.models.models import FunctionParent
24+
import os
2425

2526
if TYPE_CHECKING:
2627
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
@@ -557,25 +558,31 @@ def _extract_function_from_code(
557558

558559
def get_optimized_code_for_module(relative_path: Path, optimized_code: CodeStringsMarkdown) -> str:
559560
file_to_code_context = optimized_code.file_to_path()
560-
module_optimized_code = file_to_code_context.get(str(relative_path))
561+
str_relative = str(relative_path)
562+
module_optimized_code = file_to_code_context.get(str_relative)
561563
if module_optimized_code is None:
562564
# Fallback: if there's only one code block with None file path,
563565
# use it regardless of the expected path (the AI server doesn't always include file paths)
564-
if "None" in file_to_code_context and len(file_to_code_context) == 1:
566+
if len(file_to_code_context) == 1 and "None" in file_to_code_context:
565567
module_optimized_code = file_to_code_context["None"]
566568
logger.debug(f"Using code block with None file_path for {relative_path}")
567569
else:
568-
available_files = list(file_to_code_context.keys())
569570
# Check if this looks like a path mismatch (same filename exists under a different path)
570571
# vs the AI simply not returning code for this module
571572
requested_name = relative_path.name
572-
similar = [f for f in available_files if Path(f).name == requested_name]
573+
similar: list[str] = []
574+
# Iterate keys once and avoid constructing Path objects repeatedly
575+
for f in file_to_code_context:
576+
if os.path.basename(f) == requested_name:
577+
similar.append(f)
573578
if similar:
579+
available_files = list(file_to_code_context.keys())
574580
logger.warning(
575581
f"Optimized code not found for '{relative_path}' but found similar path(s): {similar}. "
576582
f"Re-check your markdown code structure. Available files: {available_files}"
577583
)
578584
else:
585+
available_files = list(file_to_code_context.keys())
579586
logger.debug(
580587
f"AI service did not return optimized code for '{relative_path}'. "
581588
f"Available files in response: {available_files}"

codeflash/models/models.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,12 +331,13 @@ def file_to_path(self) -> dict[str, str]:
331331
dict[str, str]: Mapping from file path (as string) to code.
332332
333333
"""
334-
if self._cache.get("file_to_path") is not None:
334+
try:
335335
return self._cache["file_to_path"]
336-
self._cache["file_to_path"] = {
337-
str(code_string.file_path): code_string.code for code_string in self.code_strings
338-
}
339-
return self._cache["file_to_path"]
336+
except KeyError:
337+
# Build the mapping once and cache it
338+
mapping = {str(code_string.file_path): code_string.code for code_string in self.code_strings}
339+
self._cache["file_to_path"] = mapping
340+
return mapping
340341

341342
@staticmethod
342343
def parse_markdown_code(markdown_code: str, expected_language: str = "python") -> CodeStringsMarkdown:

0 commit comments

Comments
 (0)