Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 11% (0.11x) speedup for build_list_reverse_order_index in nvflare/tool/job/config/config_indexer.py

⏱️ Runtime : 3.99 milliseconds 3.61 milliseconds (best of 54 runs)

📝 Explanation and details

The optimized code achieves a 10% speedup through several key optimizations:

1. Early None Check Optimization
The most significant change moves the if value is None: check before creating the KeyIndex object. This avoids expensive object creation for None values, which explains the dramatic 264% speedup in the test_list_with_all_none test case and 38.4% improvement in test_none_values_are_skipped.

2. String Processing Optimization
Replaced the verbose dot-finding logic for path parsing with Python's built-in rsplit(".", 1)[-1], eliminating multiple string operations (find(), rindex(), slicing). This optimization shows up strongly in the test_path_key_sets_component_name test (39.4% faster) and test_path_key_with_no_dot (22.3% faster).

3. List Length Check Optimization
Changed len(value) > 0 to value for non-empty list checks, avoiding the overhead of calculating list length when a simple truthiness test suffices.

4. Function Lookup Caching
Added local caching of the has_none_primitives_in_list function lookup to avoid repeated global namespace lookups in the loop.

5. Dictionary Access Optimization
In add_to_indices, split the key_indices.get(key, []) into explicit None checking to avoid creating empty lists unnecessarily and make the control flow more efficient.

The optimizations are particularly effective for:

  • Lists with many None values (up to 264% faster)
  • Path strings requiring class name extraction (up to 39% faster)
  • Large lists with primitives (9-13% faster consistently)
  • Deeply nested list structures (13-17% faster)

These optimizations maintain identical functionality while reducing computational overhead in the hot paths identified by the line profiler.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 28 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 93.1%
🌀 Generated Regression Tests and Runtime
import pytest
from nvflare.tool.job.config.config_indexer import \
    build_list_reverse_order_index


# Minimal stub for ConfigTree (since pyhocon is not available in test env)
class ConfigTree(dict):
    pass

# --------- UNIT TESTS ---------
# Basic Test Cases

def test_empty_list_returns_empty_dict():
    # Test with empty list
    codeflash_output = build_list_reverse_order_index([], "test", None, None, None); result = codeflash_output # 1.05μs -> 910ns (15.3% faster)

def test_single_primitive_element():
    # Test with single primitive element
    codeflash_output = build_list_reverse_order_index([5], "test", None, None, None); result = codeflash_output # 3.79μs -> 3.85μs (1.61% slower)

def test_multiple_primitive_elements():
    # Test with multiple primitive elements
    data = [1, "foo", 3.14, True]
    codeflash_output = build_list_reverse_order_index(data, "test", None, None, None); result = codeflash_output # 6.91μs -> 6.23μs (10.9% faster)
    for idx, val in enumerate(data):
        k = f"test[{idx}]"

def test_list_of_lists_primitives():
    # Test with nested lists of primitives
    data = [[1, 2], [3, 4]]
    codeflash_output = build_list_reverse_order_index(data, "outer", None, None, None); result = codeflash_output # 9.23μs -> 8.39μs (9.97% faster)


def test_none_values_are_skipped():
    # Test that None values are skipped
    data = [None, 5, None]
    codeflash_output = build_list_reverse_order_index(data, "test", None, None, None); result = codeflash_output # 5.94μs -> 4.29μs (38.4% faster)


def test_empty_nested_list():
    # Test with nested empty lists
    data = [[], []]
    codeflash_output = build_list_reverse_order_index(data, "outer", None, None, None); result = codeflash_output # 5.63μs -> 5.06μs (11.3% faster)


def test_list_with_unsupported_type_raises():
    # Test with unsupported type in list
    class Dummy: pass
    data = [Dummy()]
    with pytest.raises(RuntimeError):
        build_list_reverse_order_index(data, "fail", None, None, None) # 5.63μs -> 5.18μs (8.77% faster)

def test_deeply_nested_lists():
    # Test with deeply nested lists (up to 5 levels)
    data = [[[[[42]]]]]
    codeflash_output = build_list_reverse_order_index(data, "root", None, None, None); result = codeflash_output # 8.16μs -> 6.96μs (17.2% faster)

def test_name_key_on_empty_list_sets_component_name_none():
    # Test that name key on empty list sets component_name to None
    data = []
    codeflash_output = build_list_reverse_order_index(data, "name", None, None, None); result = codeflash_output # 889ns -> 778ns (14.3% faster)

def test_path_key_with_no_dot_sets_no_component_name():
    # Test that path key with no dot does not set component_name
    data = ["nodot"]
    codeflash_output = build_list_reverse_order_index(data, "path", None, None, None); result = codeflash_output # 4.10μs -> 3.35μs (22.3% faster)
    ki = result["path[0]"][0]


def test_large_list_of_primitives():
    # Test with a large list of primitives
    data = list(range(1000))
    codeflash_output = build_list_reverse_order_index(data, "big", None, None, None); result = codeflash_output # 739μs -> 675μs (9.39% faster)
    # Should index all 1000 elements
    for i in range(1000):
        k = f"big[{i}]"

def test_large_nested_lists():
    # Test with a large list of lists
    data = [[i for i in range(10)] for _ in range(100)]
    codeflash_output = build_list_reverse_order_index(data, "outer", None, None, None); result = codeflash_output # 811μs -> 741μs (9.44% faster)
    # Should index outer[0][0] through outer[99][9]
    for outer_idx in range(100):
        for inner_idx in range(10):
            k = f"outer[{outer_idx}][{inner_idx}]"



#------------------------------------------------
from typing import Dict, List, Optional

# imports
import pytest
from nvflare.tool.job.config.config_indexer import \
    build_list_reverse_order_index


# Minimal KeyIndex class for testing (simulates structure from original code)
class KeyIndex:
    def __init__(self, key, value, parent_key, index):
        self.key = key
        self.value = value
        self.parent_key = parent_key
        self.index = index
        self.component_name = None

    def __eq__(self, other):
        # Only compare key, value, index for equality in tests
        return (
            isinstance(other, KeyIndex)
            and self.key == other.key
            and self.value == other.value
            and self.index == other.index
            and self.component_name == other.component_name
        )

    def __repr__(self):
        return (
            f"KeyIndex(key={self.key!r}, value={self.value!r}, "
            f"parent_key={self.parent_key.key if self.parent_key else None!r}, "
            f"index={self.index!r}, component_name={self.component_name!r})"
        )

# Minimal ConfigTree for testing (simulates pyhocon.ConfigTree)
class ConfigTree(dict):
    pass

# ==========================
# Unit Test Suite
# ==========================

# Basic Test Cases

def test_empty_list_returns_empty_dict():
    # Test with empty list input
    codeflash_output = build_list_reverse_order_index([], "foo", None, None, None); result = codeflash_output # 1.12μs -> 945ns (18.3% faster)

def test_single_primitive_element():
    # Test with a single primitive element
    codeflash_output = build_list_reverse_order_index([42], "num", None, None, None); result = codeflash_output # 4.30μs -> 3.82μs (12.6% faster)

def test_multiple_primitives():
    # Test with multiple primitive elements
    values = [1, "a", True, 3.14]
    codeflash_output = build_list_reverse_order_index(values, "val", None, None, None); result = codeflash_output # 7.35μs -> 6.68μs (9.89% faster)
    for i, v in enumerate(values):
        key = f"val[{i}]"

def test_list_of_lists():
    # Test with nested lists
    values = [[1, 2], [3, 4]]
    codeflash_output = build_list_reverse_order_index(values, "nest", None, None, None); result = codeflash_output # 8.91μs -> 8.50μs (4.86% faster)



def test_name_key_sets_component_name():
    # Test if 'name' key sets component_name
    codeflash_output = build_list_reverse_order_index(["compA"], "name", None, None, None); result = codeflash_output # 4.89μs -> 4.46μs (9.43% faster)
    keyidx = result["name[0]"][0]

def test_path_key_sets_component_name():
    # Test if 'path' key extracts class name after last dot
    codeflash_output = build_list_reverse_order_index(["foo.bar.MyClass"], "path", None, None, None); result = codeflash_output # 5.36μs -> 3.85μs (39.4% faster)
    keyidx = result["path[0]"][0]

def test_none_in_list_skipped():
    # Test that None values are skipped
    codeflash_output = build_list_reverse_order_index([None, 7], "foo", None, None, None); result = codeflash_output # 4.54μs -> 3.41μs (33.3% faster)

def test_unhandled_type_raises():
    # Test that unhandled types raise RuntimeError
    class Dummy: pass
    with pytest.raises(RuntimeError):
        build_list_reverse_order_index([Dummy()], "foo", None, None, None) # 4.54μs -> 4.16μs (9.06% faster)

# Edge Test Cases

def test_empty_inner_list():
    # Test with empty inner list
    codeflash_output = build_list_reverse_order_index([[]], "outer", None, None, None); result = codeflash_output # 3.57μs -> 3.24μs (10.3% faster)



def test_deeply_nested_lists():
    # Test with lists nested 3 levels deep
    values = [[[1], [2]], [[3], [4]]]
    codeflash_output = build_list_reverse_order_index(values, "deep", None, None, None); result = codeflash_output # 13.6μs -> 12.1μs (13.0% faster)
    # Should index all primitives at the deepest level
    for i in range(2):
        for j in range(2):
            key = f"deep[{i}][{j}][0]"


def test_list_with_all_none():
    # Test with all None values
    codeflash_output = build_list_reverse_order_index([None, None], "foo", None, None, None); result = codeflash_output # 3.98μs -> 1.09μs (264% faster)

def test_name_key_with_empty_string():
    # Test 'name' key with empty string
    codeflash_output = build_list_reverse_order_index([""], "name", None, None, None); result = codeflash_output # 4.01μs -> 4.20μs (4.45% slower)
    keyidx = result["name[0]"][0]

def test_path_key_with_no_dot():
    # Test 'path' key with string with no dot
    codeflash_output = build_list_reverse_order_index(["NoDot"], "path", None, None, None); result = codeflash_output # 3.78μs -> 3.48μs (8.60% faster)
    keyidx = result["path[0]"][0]



def test_large_flat_list():
    # Test with large flat list of primitives
    values = list(range(1000))
    codeflash_output = build_list_reverse_order_index(values, "big", None, None, None); result = codeflash_output # 759μs -> 671μs (13.1% faster)
    for i in range(0, 1000, 200):  # Sample some keys
        key = f"big[{i}]"

def test_large_nested_lists():
    # Test with a list of 100 lists, each with 10 elements
    values = [[j for j in range(10)] for i in range(100)]
    codeflash_output = build_list_reverse_order_index(values, "outer", None, None, None); result = codeflash_output # 812μs -> 742μs (9.43% faster)
    for i in range(0, 100, 20):
        for j in range(0, 10, 5):
            key = f"outer[{i}][{j}]"



def test_performance_large_list():
    # Test that function completes quickly for large list (not a true perf test, but checks no hang)
    import time
    values = [i for i in range(1000)]
    start = time.time()
    codeflash_output = build_list_reverse_order_index(values, "speed", None, None, None); result = codeflash_output # 742μs -> 670μs (10.8% faster)
    elapsed = time.time() - start
# 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-build_list_reverse_order_index-mhe35wfp and push.

Codeflash Static Badge

The optimized code achieves a 10% speedup through several key optimizations:

**1. Early None Check Optimization**
The most significant change moves the `if value is None:` check before creating the `KeyIndex` object. This avoids expensive object creation for None values, which explains the dramatic 264% speedup in the `test_list_with_all_none` test case and 38.4% improvement in `test_none_values_are_skipped`.

**2. String Processing Optimization**
Replaced the verbose dot-finding logic for path parsing with Python's built-in `rsplit(".", 1)[-1]`, eliminating multiple string operations (`find()`, `rindex()`, slicing). This optimization shows up strongly in the `test_path_key_sets_component_name` test (39.4% faster) and `test_path_key_with_no_dot` (22.3% faster).

**3. List Length Check Optimization**
Changed `len(value) > 0` to `value` for non-empty list checks, avoiding the overhead of calculating list length when a simple truthiness test suffices.

**4. Function Lookup Caching**
Added local caching of the `has_none_primitives_in_list` function lookup to avoid repeated global namespace lookups in the loop.

**5. Dictionary Access Optimization**
In `add_to_indices`, split the `key_indices.get(key, [])` into explicit None checking to avoid creating empty lists unnecessarily and make the control flow more efficient.

The optimizations are particularly effective for:
- Lists with many None values (up to 264% faster)
- Path strings requiring class name extraction (up to 39% faster) 
- Large lists with primitives (9-13% faster consistently)
- Deeply nested list structures (13-17% faster)

These optimizations maintain identical functionality while reducing computational overhead in the hot paths identified by the line profiler.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 30, 2025 23:59
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 30, 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 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant