From 1c3ccd4fb4e18aad7c05bb2b59b10137ced85546 Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Sat, 19 Apr 2025 20:05:09 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Speed=20up=20method=20`Qua?= =?UTF-8?q?lifiedFunctionUsageMarker.=5Fexpand=5Fqualified=5Ffunctions`=20?= =?UTF-8?q?by=2053%=20in=20PR=20#161=20(`benchmark-docs`)=20To=20optimize?= =?UTF-8?q?=20this=20program,=20we'll=20focus=20on=20reducing=20the=20runt?= =?UTF-8?q?ime=20of=20the=20`=5Fexpand=5Fqualified=5Ffunctions`=20method.?= =?UTF-8?q?=20Let's=20analyze=20the=20main=20performance=20issues=20based?= =?UTF-8?q?=20on=20the=20provided=20profiling=20results.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. The loop `for name in self.definitions` is called 22,095,676 times, which is significantly higher than the outer loop iterations (3,396), suggesting inefficiency in handling `self.definitions`. 2. The `name.startswith(f"{class_name}.__") and name.endswith("__")` checks are done multiple times and each check is quite expensive within the high number of iterations. ### Optimizations. 1. Use more efficient data structures: - Convert `self.definitions` to a preprocessed set or dictionary to quickly check for dunder methods. 2. Preprocess the definitions only once. - Instead of checking `name.startswith(f"{class_name}.__") and name.endswith("__")` inside the loop, preprocess the `self.definitions` to filter and classify dunder methods by class names. ### Optimized Code. ### Explanation. 1. We preprocess the definitions once in the `_preprocess_definitions` function to categorize dunder methods by their class names. 2. Reuse this preprocessed data in the `_expand_qualified_functions` function to check and expand dunder methods more efficiently. This significantly reduces the complexity of the loops and the number of checks required during the expansion process. --- .../context/unused_definition_remover.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/codeflash/context/unused_definition_remover.py b/codeflash/context/unused_definition_remover.py index bfcbbaead..6ba299a01 100644 --- a/codeflash/context/unused_definition_remover.py +++ b/codeflash/context/unused_definition_remover.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections import defaultdict from dataclasses import dataclass, field import libcst as cst @@ -255,6 +256,7 @@ class QualifiedFunctionUsageMarker: def __init__(self, definitions: dict[str, UsageInfo], qualified_function_names: set[str]) -> None: self.definitions = definitions self.qualified_function_names = qualified_function_names + self.class_dunder_methods = self._preprocess_definitions() self.expanded_qualified_functions = self._expand_qualified_functions() def _expand_qualified_functions(self) -> set[str]: @@ -262,7 +264,7 @@ def _expand_qualified_functions(self) -> set[str]: expanded = set(self.qualified_function_names) # Find class methods and add their containing classes and dunder methods - for qualified_name in list(self.qualified_function_names): + for qualified_name in self.qualified_function_names: if "." in qualified_name: class_name, method_name = qualified_name.split(".", 1) @@ -270,9 +272,8 @@ def _expand_qualified_functions(self) -> set[str]: expanded.add(class_name) # Add all dunder methods of the class - for name in self.definitions: - if name.startswith(f"{class_name}.__") and name.endswith("__"): - expanded.add(name) + if class_name in self.class_dunder_methods: + expanded.update(self.class_dunder_methods[class_name]) return expanded @@ -301,9 +302,21 @@ def mark_as_used_recursively(self, name: str) -> None: for dep in self.definitions[name].dependencies: self.mark_as_used_recursively(dep) + def _preprocess_definitions(self) -> dict[str, set[str]]: + """Preprocess definitions to find dunder methods for each class.""" + class_dunder_methods = defaultdict(set) + + for name in self.definitions: + if name.count(".") == 1: + class_name, method_name = name.split(".", 1) + if method_name.startswith("__") and method_name.endswith("__"): + class_dunder_methods[class_name].add(name) + + return class_dunder_methods + def remove_unused_definitions_recursively( - node: cst.CSTNode, definitions: dict[str, UsageInfo] + node: cst.CSTNode, definitions: dict[str, UsageInfo] ) -> tuple[cst.CSTNode | None, bool]: """Recursively filter the node to remove unused definitions. @@ -358,7 +371,10 @@ def remove_unused_definitions_recursively( names = extract_names_from_targets(target.target) for name in names: class_var_name = f"{class_name}.{name}" - if class_var_name in definitions and definitions[class_var_name].used_by_qualified_function: + if ( + class_var_name in definitions + and definitions[class_var_name].used_by_qualified_function + ): var_used = True method_or_var_used = True break