From cb999d4091477ef8531c53355b33490e8905d3dc Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 01:04:19 +0000 Subject: [PATCH] Optimize convert_parents_to_tuple MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brief: The optimized function cuts the original 116 ms wall time down to 5.29 ms (≈2092% relative speedup) by adding a fast-path that detects when the input already contains FunctionParent instances and avoids rebuilding them. This reduces the dominant cost (many pydantic/dataclass constructions and memory allocations) and is why runtime falls by orders of magnitude. What changed (concrete): - Fast-path checks: - If input is a tuple and every element is already a FunctionParent, return it directly. - If input is a list and every element is already a FunctionParent, convert to tuple(parents) (no per-element reconstruction). - Early empty-sequence checks return tuple() immediately. - Fallback: only if elements are not FunctionParent, construct new FunctionParent instances as before. Why this is faster (technical reasons): - Dataclass/pydantic construction is relatively expensive. The original always called FunctionParent(...) for every element; that meant constructor validation and allocation for each item even when elements were already the correct type. Avoiding those calls eliminates that work. - Fewer memory allocations: returning the existing tuple (or making a shallow tuple from an existing list) avoids allocating N new FunctionParent objects and reduces GC pressure. - Reduced Python-level overhead: generator-driven per-item construction and attribute reads are removed in the common case. - The line profiler confirms the shift: original time is concentrated in the single construction line; optimized profiling shows most work moved to inexpensive isinstance/all checks and a much smaller remainder for construction only when needed. Observed trade-offs / behavior notes: - Error cases that hit the fallback (inputs that lack .name/.type) do a bit more work (extra isinstance checks) before failing; annotated tests show those cases became slightly slower (~25–30% slower). This is a reasonable trade-off because the common/correct case is dramatically faster. - The function now may return the input tuple object unchanged (instead of always returning a freshly-built tuple of new FunctionParent instances). Because FunctionParent is a frozen dataclass here, returning the same instances is safe in practice; however, callers that relied on identity-new objects (is-comparisons or expecting brand-new instances) should be aware of this subtle change. Where this helps most (based on tests and profiles): - Large inputs: converting lists of 1000 FunctionParent instances fell from ~1.15 ms to ~46 μs (huge win). - Repeated conversions in hot paths: a loop of 1000 conversions on a medium-sized list went from ~110 ms to ~4.63 ms — this function is now far cheaper in tight loops. - Small common cases (non-empty lists/tuples of FunctionParent) are severalx faster (see many annotated tests with >2x up to 20x speedups). Summary: The optimization targets the dominant cost — reconstructing validated dataclass instances — by detecting when that work is unnecessary and short-circuiting it. That yields the large runtime improvement while preserving correctness for the common use cases; the only notable trade-off is slightly slower failure paths and the semantic change of preserving existing instances rather than producing brand-new ones (which is safe here because FunctionParent is frozen). --- codeflash/languages/base.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/codeflash/languages/base.py b/codeflash/languages/base.py index ae439bf6c..972c8658b 100644 --- a/codeflash/languages/base.py +++ b/codeflash/languages/base.py @@ -800,4 +800,17 @@ def convert_parents_to_tuple(parents: list[Any] | tuple[Any, ...]) -> tuple[Func Tuple of FunctionParent objects. """ + # Fast path: if all items are already FunctionParent, avoid reconstructing them. + if isinstance(parents, tuple): + if parents and all(isinstance(p, FunctionParent) for p in parents): + return parents # type: ignore[return-value] + if not parents: + return tuple() + elif isinstance(parents, list): + if parents and all(isinstance(p, FunctionParent) for p in parents): + return tuple(parents) + if not parents: + return tuple() + + # Fallback: build a new tuple of FunctionParent instances from provided objects. return tuple(FunctionParent(name=p.name, type=p.type) for p in parents)