Skip to content

⚡️ Speed up function generate_full_patch by 2,435%#38

Open
codeflash-ai[bot] wants to merge 1 commit intomainfrom
codeflash/optimize-generate_full_patch-mgvyl446
Open

⚡️ Speed up function generate_full_patch by 2,435%#38
codeflash-ai[bot] wants to merge 1 commit intomainfrom
codeflash/optimize-generate_full_patch-mgvyl446

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai bot commented Oct 18, 2025

📄 2,435% (24.35x) speedup for generate_full_patch in pr_agent/algo/pr_processing.py

⏱️ Runtime : 205 milliseconds 8.09 milliseconds (best of 424 runs)

📝 Explanation and details

The optimized code achieves a 24x speedup by eliminating expensive repeated function calls in the hot loop. The key optimizations are:

1. Cached Configuration Access

  • Moved get_settings().config.verbosity_level calls outside the loop and cached the value as verbosity_level
  • The line profiler shows the original code spent 96.4% of total time (1.27s out of 1.28s) on these repeated get_settings() calls inside the loop
  • In the optimized version, this drops to 90.1% of a much smaller total time (48ms out of 53ms), representing the one-time setup cost

2. Pre-computed Threshold Values

  • Cached max_tokens_model - OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD and max_tokens_model - OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD as hard_threshold and soft_threshold
  • Eliminates repeated arithmetic operations in conditional checks

3. Set-based Membership Testing

  • Converted remaining_files_list_prev to a set (prev_files_set) for O(1) membership testing instead of O(n) list lookups
  • Particularly beneficial for large file lists, as shown in test cases with 500+ files where speedup reaches 344x

Why This Works:
Python's get_settings() function involves context lookups and attribute traversal that are expensive when called repeatedly. The optimized version performs these operations once and reuses the cached values, transforming an O(n) complexity per iteration into O(1).

Best Performance Gains:
The optimization excels with large file counts and high verbosity levels where the expensive logging conditions are frequently evaluated. Test cases show speedups of 13x-344x for scenarios with 100+ files, while smaller test cases show modest slowdowns due to the caching overhead.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 38 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 89.3%
🌀 Generated Regression Tests and Runtime
import pytest
from pr_agent.algo.pr_processing import generate_full_patch

# ---- Mock Classes ----

class DummyTokenHandler:
    def __init__(self, prompt_tokens=0, tokens_map=None):
        self.prompt_tokens = prompt_tokens
        self.tokens_map = tokens_map or {}

    def count_tokens(self, patch, force_accurate=False):
        # Return the number of tokens for the patch, or just the length as a fallback
        return self.tokens_map.get(patch, len(patch))

# ---- Basic Test Cases ----

def test_single_file_patch_basic():
    # Basic: One file, patch fits within token limits
    file_dict = {
        "file1.py": {
            "patch": "diff --git a/file1.py b/file1.py\n+print('hello world')",
            "tokens": 10,
            "edit_type": "modified"
        }
    }
    handler = DummyTokenHandler(prompt_tokens=5, tokens_map={
        "\n\n## File: 'file1.py'\n\ndiff --git a/file1.py b/file1.py\n+print('hello world')\n": 10
    })
    codeflash_output = generate_full_patch(False, file_dict, 100, ["file1.py"], handler); result = codeflash_output # 54.1μs -> 210μs (74.2% slower)

def test_multiple_files_all_fit():
    # Basic: Multiple files, all fit within token limits
    file_dict = {
        "a.py": {"patch": "patchA", "tokens": 10, "edit_type": "modified"},
        "b.py": {"patch": "patchB", "tokens": 20, "edit_type": "added"}
    }
    handler = DummyTokenHandler(prompt_tokens=5, tokens_map={
        "\n\n## File: 'a.py'\n\npatchA\n": 10,
        "\n\n## File: 'b.py'\n\npatchB\n": 20
    })
    codeflash_output = generate_full_patch(False, file_dict, 100, ["a.py", "b.py"], handler); result = codeflash_output # 96.7μs -> 207μs (53.4% slower)

def test_convert_hunks_to_line_numbers_true():
    # Basic: convert_hunks_to_line_numbers True, patch formatting changes
    file_dict = {
        "x.py": {"patch": "patchX", "tokens": 5, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map={
        "\n\npatchX": 5
    })
    codeflash_output = generate_full_patch(True, file_dict, 20, ["x.py"], handler); result = codeflash_output # 53.1μs -> 206μs (74.3% slower)

def test_file_not_in_remaining_files_list_prev():
    # Basic: file not in remaining_files_list_prev is skipped
    file_dict = {
        "skipme.py": {"patch": "patch", "tokens": 5, "edit_type": "modified"},
        "keepme.py": {"patch": "patch2", "tokens": 5, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map={
        "\n\n## File: 'keepme.py'\n\npatch2\n": 5
    })
    codeflash_output = generate_full_patch(False, file_dict, 20, ["keepme.py"], handler); result = codeflash_output # 52.6μs -> 207μs (74.6% slower)

# ---- Edge Test Cases ----

def test_hard_token_threshold_skips_all():
    # Edge: prompt_tokens already exceeds hard threshold, all files skipped
    file_dict = {
        "file1.py": {"patch": "patch", "tokens": 5, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=95)
    # max_tokens_model = 100, hard threshold = 1000, so always skipped
    codeflash_output = generate_full_patch(False, file_dict, 100, ["file1.py"], handler); result = codeflash_output # 55.6μs -> 206μs (73.0% slower)

def test_soft_token_threshold_skips_patch():
    # Edge: patch would exceed soft threshold, so file is added to remaining_files_list_new
    file_dict = {
        "bigfile.py": {"patch": "bigpatch", "tokens": 90, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=10)
    # max_tokens_model = 100, soft threshold = 1500, so always skipped
    codeflash_output = generate_full_patch(False, file_dict, 100, ["bigfile.py"], handler); result = codeflash_output # 53.7μs -> 206μs (73.9% slower)

def test_empty_patch_string():
    # Edge: patch is empty string, should not be added to patches
    file_dict = {
        "empty.py": {"patch": "", "tokens": 5, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0)
    codeflash_output = generate_full_patch(False, file_dict, 100, ["empty.py"], handler); result = codeflash_output # 54.2μs -> 206μs (73.7% slower)

def test_patch_exactly_on_soft_threshold():
    # Edge: patch tokens exactly at the soft threshold limit
    file_dict = {
        "file.py": {"patch": "patch", "tokens": 90, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=10)
    # max_tokens_model = 100, soft threshold = 1500, so always skipped
    codeflash_output = generate_full_patch(False, file_dict, 100, ["file.py"], handler); result = codeflash_output # 53.9μs -> 205μs (73.7% slower)

def test_patch_exactly_on_hard_threshold():
    # Edge: prompt_tokens exactly at hard threshold, file skipped
    file_dict = {
        "file.py": {"patch": "patch", "tokens": 5, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=100)
    # max_tokens_model = 100, hard threshold = 1000, so always skipped
    codeflash_output = generate_full_patch(False, file_dict, 100, ["file.py"], handler); result = codeflash_output # 52.6μs -> 205μs (74.4% slower)

def test_patch_none():
    # Edge: patch is None, should not be added
    file_dict = {
        "none.py": {"patch": None, "tokens": 5, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0)
    codeflash_output = generate_full_patch(False, file_dict, 100, ["none.py"], handler); result = codeflash_output # 55.6μs -> 205μs (72.9% slower)

def test_file_with_zero_tokens():
    # Edge: file with zero tokens, should be included if patch exists
    file_dict = {
        "zero.py": {"patch": "patch", "tokens": 0, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map={
        "\n\n## File: 'zero.py'\n\npatch\n": 0
    })
    codeflash_output = generate_full_patch(False, file_dict, 100, ["zero.py"], handler); result = codeflash_output # 55.0μs -> 206μs (73.3% slower)

def test_patch_with_whitespace_only():
    # Edge: patch is whitespace only, should be added if not empty string
    file_dict = {
        "space.py": {"patch": "   ", "tokens": 1, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map={
        "\n\n## File: 'space.py'\n\n   \n": 1
    })
    codeflash_output = generate_full_patch(False, file_dict, 100, ["space.py"], handler); result = codeflash_output # 55.1μs -> 204μs (73.0% slower)

def test_patch_tokens_negative():
    # Edge: negative tokens, should still add patch if patch exists
    file_dict = {
        "neg.py": {"patch": "patch", "tokens": -10, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map={
        "\n\n## File: 'neg.py'\n\npatch\n": 5
    })
    codeflash_output = generate_full_patch(False, file_dict, 100, ["neg.py"], handler); result = codeflash_output # 55.2μs -> 206μs (73.2% slower)

def test_file_dict_empty():
    # Edge: file_dict is empty, should return empty results
    file_dict = {}
    handler = DummyTokenHandler(prompt_tokens=0)
    codeflash_output = generate_full_patch(False, file_dict, 100, [], handler); result = codeflash_output # 765ns -> 204μs (99.6% slower)

# ---- Large Scale Test Cases ----

def test_many_files_all_fit():
    # Large scale: 500 files, all fit within token limits
    file_dict = {}
    tokens_map = {}
    for i in range(500):
        fname = f"f{i}.py"
        patch = f"patch{i}"
        file_dict[fname] = {"patch": patch, "tokens": 1, "edit_type": "modified"}
        tokens_map[f"\n\n## File: '{fname}'\n\n{patch}\n"] = 1
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map=tokens_map)
    remaining_files = [f"f{i}.py" for i in range(500)]
    codeflash_output = generate_full_patch(False, file_dict, 1000, remaining_files, handler); result = codeflash_output # 93.7ms -> 271μs (34444% faster)

def test_many_files_some_skipped_by_soft_threshold():
    # Large scale: 500 files, some skipped due to soft threshold
    file_dict = {}
    tokens_map = {}
    for i in range(500):
        fname = f"f{i}.py"
        patch = f"patch{i}"
        # Make some files very large
        tokens = 1 if i < 495 else 2000
        file_dict[fname] = {"patch": patch, "tokens": tokens, "edit_type": "modified"}
        tokens_map[f"\n\n## File: '{fname}'\n\n{patch}\n"] = tokens
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map=tokens_map)
    remaining_files = [f"f{i}.py" for i in range(500)]
    codeflash_output = generate_full_patch(False, file_dict, 10000, remaining_files, handler); result = codeflash_output # 94.3ms -> 349μs (26851% faster)

def test_many_files_hard_threshold_triggers():
    # Large scale: prompt_tokens already near hard threshold, all skipped
    file_dict = {}
    for i in range(100):
        fname = f"file{i}.py"
        file_dict[fname] = {"patch": f"patch{i}", "tokens": 1, "edit_type": "modified"}
    handler = DummyTokenHandler(prompt_tokens=950)
    remaining_files = [f"file{i}.py" for i in range(100)]
    codeflash_output = generate_full_patch(False, file_dict, 1000, remaining_files, handler); result = codeflash_output # 3.03ms -> 219μs (1283% faster)

def test_large_patch_content():
    # Large scale: one file with large patch content, but tokens fit
    big_patch = "a" * 900
    file_dict = {
        "big.py": {"patch": big_patch, "tokens": 900, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0, tokens_map={
        f"\n\n## File: 'big.py'\n\n{big_patch}\n": 900
    })
    codeflash_output = generate_full_patch(False, file_dict, 1000, ["big.py"], handler); result = codeflash_output # 234μs -> 207μs (13.1% faster)

def test_large_patch_exceeds_soft_threshold():
    # Large scale: one file, patch exceeds soft threshold, should be skipped
    big_patch = "b" * 2000
    file_dict = {
        "huge.py": {"patch": big_patch, "tokens": 2000, "edit_type": "modified"}
    }
    handler = DummyTokenHandler(prompt_tokens=0)
    codeflash_output = generate_full_patch(False, file_dict, 1000, ["huge.py"], handler); result = codeflash_output # 211μs -> 205μs (2.91% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import pytest
from pr_agent.algo.pr_processing import generate_full_patch

# --- Minimal stubs and mocks for dependencies ---

# Minimal stub for get_logger()
class DummyLogger:
    def __init__(self):
        self.warnings = []
        self.infos = []
    def warning(self, msg):
        self.warnings.append(msg)
    def info(self, msg):
        self.infos.append(msg)
_dummy_logger = DummyLogger()

# Minimal stub for get_settings()
class DummyConfig:
    def __init__(self, model="gpt-3", verbosity_level=0):
        self.model = model
        self.verbosity_level = verbosity_level
class DummySettings:
    def __init__(self, model="gpt-3", verbosity_level=0):
        self.config = DummyConfig(model, verbosity_level)
    def get(self, key):
        # For openai.key or anthropic.key
        return "dummy-key"
_dummy_settings = DummySettings()

# Minimal stub for token_handler
class DummyTokenHandler:
    def __init__(self, prompt_tokens=0, token_map=None):
        self.prompt_tokens = prompt_tokens
        self._token_map = token_map or {}
        self._count_tokens_calls = []
    def count_tokens(self, patch_final, force_accurate=False):
        # Record calls for test introspection
        self._count_tokens_calls.append(patch_final)
        # Return mapped value or default to 1 token per char
        return self._token_map.get(patch_final, len(patch_final))

# --- The function under test (copied from above, with dependency injection for testability) ---
OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD = 1500
OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD = 1000
from pr_agent.algo.pr_processing import generate_full_patch

# 1. Basic Test Cases

def test_basic_single_file_included():
    """Test with a single file, patch fits comfortably within token limits."""
    file_dict = {
        "file1.py": {"patch": "diff --git ...", "tokens": 10, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=5, token_map={"\n\n## File: 'file1.py'\n\ndiff --git ...\n": 10})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=1000,
        remaining_files_list_prev=["file1.py"],
        token_handler=token_handler
    ) # 79.4μs -> 207μs (61.8% slower)

def test_basic_multiple_files_all_fit():
    """Test with multiple files, all patches fit within token limits."""
    file_dict = {
        "a.py": {"patch": "patchA", "tokens": 10, "edit_type": "added"},
        "b.py": {"patch": "patchB", "tokens": 20, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map={
        "\n\n## File: 'a.py'\n\npatchA\n": 10,
        "\n\n## File: 'b.py'\n\npatchB\n": 20
    })
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=1000,
        remaining_files_list_prev=["a.py", "b.py"],
        token_handler=token_handler
    ) # 413μs -> 206μs (100% faster)

def test_basic_convert_hunks_to_line_numbers_true():
    """Test with convert_hunks_to_line_numbers=True, patch format should change."""
    file_dict = {
        "foo.txt": {"patch": "+++foo", "tokens": 5, "edit_type": "added"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map={"\n\n+++foo": 5})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=True,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["foo.txt"],
        token_handler=token_handler
    ) # 74.1μs -> 202μs (63.5% slower)

def test_basic_patch_is_empty():
    """Test with a file whose patch is an empty string."""
    file_dict = {
        "empty.py": {"patch": "", "tokens": 0, "edit_type": "removed"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["empty.py"],
        token_handler=token_handler
    ) # 61.6μs -> 205μs (70.0% slower)

def test_basic_file_not_in_remaining_files_list_prev():
    """Test file_dict contains a file not in remaining_files_list_prev; should be skipped."""
    file_dict = {
        "keep.py": {"patch": "patch", "tokens": 10, "edit_type": "modified"},
        "skip.py": {"patch": "patch", "tokens": 10, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map={"\n\n## File: 'keep.py'\n\npatch\n": 10})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["keep.py"],
        token_handler=token_handler
    ) # 59.3μs -> 206μs (71.3% slower)

# 2. Edge Test Cases

def test_edge_total_tokens_exceeds_hard_threshold():
    """Test where initial prompt_tokens already exceed hard threshold; file is skipped."""
    file_dict = {
        "bigfile.py": {"patch": "patch", "tokens": 10, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=950)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=1900,  # 1900 - 1000 = 900; prompt_tokens=950 > 900
        remaining_files_list_prev=["bigfile.py"],
        token_handler=token_handler
    ) # 57.0μs -> 205μs (72.3% slower)

def test_edge_patch_triggers_soft_threshold_skip():
    """Test where patch would push total_tokens over soft threshold, so file is skipped and added to remaining_files_list_new."""
    file_dict = {
        "almost.py": {"patch": "patch", "tokens": 1000, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=100, token_map={"\n\n## File: 'almost.py'\n\npatch\n": 1000})
    _dummy_settings.config.verbosity_level = 2  # Enable warnings
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=2600,  # 2600 - 1500 = 1100; 100+1000=1100, right at the edge
        remaining_files_list_prev=["almost.py"],
        token_handler=token_handler
    ) # 233μs -> 208μs (11.9% faster)

def test_edge_patch_exactly_fills_soft_threshold():
    """Test where patch exactly fills up to the soft threshold, should be included."""
    file_dict = {
        "edge.py": {"patch": "patch", "tokens": 1000, "edit_type": "modified"}
    }
    # max_tokens_model - soft = 2500, prompt_tokens = 1000, patch tokens = 1000, total = 2000 < 2500, so included
    token_handler = DummyTokenHandler(prompt_tokens=1000, token_map={"\n\n## File: 'edge.py'\n\npatch\n": 1000})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=4000,
        remaining_files_list_prev=["edge.py"],
        token_handler=token_handler
    ) # 214μs -> 208μs (2.74% faster)

def test_edge_patch_with_zero_tokens():
    """Test where a patch has zero tokens."""
    file_dict = {
        "zero.py": {"patch": "patch", "tokens": 0, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map={"\n\n## File: 'zero.py'\n\npatch\n": 0})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["zero.py"],
        token_handler=token_handler
    ) # 75.1μs -> 206μs (63.6% slower)

def test_edge_empty_file_dict():
    """Test with empty file_dict."""
    file_dict = {}
    token_handler = DummyTokenHandler(prompt_tokens=0)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=[],
        token_handler=token_handler
    ) # 1.12μs -> 205μs (99.5% slower)

def test_edge_remaining_files_list_prev_empty():
    """Test with non-empty file_dict but empty remaining_files_list_prev."""
    file_dict = {
        "foo.py": {"patch": "patch", "tokens": 10, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=[],
        token_handler=token_handler
    ) # 1.18μs -> 204μs (99.4% slower)

def test_edge_patch_none_patch_key():
    """Test file_dict with patch=None."""
    file_dict = {
        "none.py": {"patch": None, "tokens": 10, "edit_type": "modified"}
    }
    token_handler = DummyTokenHandler(prompt_tokens=0)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["none.py"],
        token_handler=token_handler
    ) # 67.2μs -> 207μs (67.6% slower)

def test_edge_patch_with_whitespace_filename():
    """Test that filename with leading/trailing whitespace is stripped in output."""
    file_dict = {
        "  weird.py  ": {"patch": "patch", "tokens": 10, "edit_type": "modified"}
    }
    expected_patch = "\n\n## File: 'weird.py'\n\npatch\n"
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map={expected_patch: 10})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["  weird.py  "],
        token_handler=token_handler
    ) # 60.4μs -> 205μs (70.6% slower)

def test_edge_patch_with_non_ascii_filename_and_patch():
    """Test with non-ASCII characters in filename and patch."""
    file_dict = {
        "üñîçødë.py": {"patch": "Διφφ", "tokens": 5, "edit_type": "modified"}
    }
    expected_patch = "\n\n## File: 'üñîçødë.py'\n\nΔιφφ\n"
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map={expected_patch: 5})
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=["üñîçødë.py"],
        token_handler=token_handler
    ) # 59.0μs -> 205μs (71.3% slower)

# 3. Large Scale Test Cases

def test_large_many_files_all_fit():
    """Test with 100 files, all fit within token limits."""
    file_dict = {f"file{i}.py": {"patch": f"patch{i}", "tokens": 1, "edit_type": "modified"} for i in range(100)}
    token_map = {f"\n\n## File: 'file{i}.py'\n\npatch{i}\n": 1 for i in range(100)}
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map=token_map)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=200,
        remaining_files_list_prev=[f"file{i}.py" for i in range(100)],
        token_handler=token_handler
    ) # 3.04ms -> 217μs (1294% faster)

def test_large_some_files_skipped_due_to_soft_threshold():
    """Test with 10 files, but total tokens only allow 5 to fit before hitting soft threshold."""
    file_dict = {f"file{i}.py": {"patch": f"patch{i}", "tokens": 100, "edit_type": "modified"} for i in range(10)}
    token_map = {f"\n\n## File: 'file{i}.py'\n\npatch{i}\n": 100 for i in range(10)}
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map=token_map)
    # max_tokens_model - soft = 1000, so only 10 files * 100 tokens = 1000, but after 5 files, next would exceed
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=1500,
        remaining_files_list_prev=[f"file{i}.py" for i in range(10)],
        token_handler=token_handler
    ) # 1.93ms -> 207μs (828% faster)
    # The first 5 files are included, the rest are skipped
    included = [f"file{i}.py" for i in range(5)]
    skipped = [f"file{i}.py" for i in range(5, 10)]

def test_large_all_files_exceed_soft_threshold():
    """Test with 10 files, each patch is so large that none can be included."""
    file_dict = {f"file{i}.py": {"patch": f"patch{i}", "tokens": 2000, "edit_type": "modified"} for i in range(10)}
    token_map = {f"\n\n## File: 'file{i}.py'\n\npatch{i}\n": 2000 for i in range(10)}
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map=token_map)
    _dummy_settings.config.verbosity_level = 2
    # max_tokens_model - soft = 1000, each file needs 2000 tokens, so all skipped
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=2500,
        remaining_files_list_prev=[f"file{i}.py" for i in range(10)],
        token_handler=token_handler
    ) # 1.90ms -> 207μs (817% faster)

def test_large_initial_prompt_tokens_near_hard_threshold():
    """Test with 100 files, but initial prompt_tokens almost at hard threshold, so all skipped."""
    file_dict = {f"file{i}.py": {"patch": f"patch{i}", "tokens": 1, "edit_type": "modified"} for i in range(100)}
    token_map = {f"\n\n## File: 'file{i}.py'\n\npatch{i}\n": 1 for i in range(100)}
    token_handler = DummyTokenHandler(prompt_tokens=1501, token_map=token_map)
    # max_tokens_model - hard = 1000, so if prompt_tokens > 1000, all skipped
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=False,
        file_dict=file_dict,
        max_tokens_model=2500,
        remaining_files_list_prev=[f"file{i}.py" for i in range(100)],
        token_handler=token_handler
    ) # 3.10ms -> 219μs (1313% faster)

def test_large_convert_hunks_to_line_numbers_true():
    """Test with 50 files, convert_hunks_to_line_numbers=True, all patches included."""
    file_dict = {f"file{i}.py": {"patch": f"patch{i}", "tokens": 1, "edit_type": "modified"} for i in range(50)}
    token_map = {f"\n\npatch{i}": 1 for i in range(50)}
    token_handler = DummyTokenHandler(prompt_tokens=0, token_map=token_map)
    total_tokens, patches, remaining_files_list_new, files_in_patch_list = generate_full_patch(
        convert_hunks_to_line_numbers=True,
        file_dict=file_dict,
        max_tokens_model=100,
        remaining_files_list_prev=[f"file{i}.py" for i in range(50)],
        token_handler=token_handler
    ) # 1.42ms -> 208μs (578% faster)
    for i in range(50):
        pass
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-generate_full_patch-mgvyl446 and push.

Codeflash

The optimized code achieves a **24x speedup** by eliminating expensive repeated function calls in the hot loop. The key optimizations are:

**1. Cached Configuration Access**
- Moved `get_settings().config.verbosity_level` calls outside the loop and cached the value as `verbosity_level`
- The line profiler shows the original code spent **96.4% of total time** (1.27s out of 1.28s) on these repeated `get_settings()` calls inside the loop
- In the optimized version, this drops to **90.1%** of a much smaller total time (48ms out of 53ms), representing the one-time setup cost

**2. Pre-computed Threshold Values**
- Cached `max_tokens_model - OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD` and `max_tokens_model - OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD` as `hard_threshold` and `soft_threshold`
- Eliminates repeated arithmetic operations in conditional checks

**3. Set-based Membership Testing**
- Converted `remaining_files_list_prev` to a set (`prev_files_set`) for O(1) membership testing instead of O(n) list lookups
- Particularly beneficial for large file lists, as shown in test cases with 500+ files where speedup reaches **344x**

**Why This Works:**
Python's `get_settings()` function involves context lookups and attribute traversal that are expensive when called repeatedly. The optimized version performs these operations once and reuses the cached values, transforming an O(n) complexity per iteration into O(1).

**Best Performance Gains:**
The optimization excels with **large file counts** and **high verbosity levels** where the expensive logging conditions are frequently evaluated. Test cases show speedups of 13x-344x for scenarios with 100+ files, while smaller test cases show modest slowdowns due to the caching overhead.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 18, 2025 07:31
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants