Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 21, 2025

📄 15% (0.15x) speedup for Algorithms.list in src/titiler/core/titiler/core/algorithm/__init__.py

⏱️ Runtime : 46.6 microseconds 40.6 microseconds (best of 250 runs)

📝 Explanation and details

The optimization replaces list(self.data.keys()) with [*self.data] (unpacking operator), achieving a 14% speedup by eliminating the intermediate .keys() view object creation.

Key Changes:

  • Direct unpacking: [*self.data] directly unpacks dictionary keys into a list
  • Eliminated intermediate step: Removes the .keys() method call and list() constructor overhead
  • Added explicit attribute definition: Added data = attr.ib() for proper attrs compatibility

Why It's Faster:
In Python, dict.keys() creates a dictionary view object that must then be converted to a list. The unpacking operator [*self.data] bypasses this intermediate step by directly iterating over the dictionary keys during list construction, reducing function call overhead and object creation.

Performance Context:
Based on function_references, this method is called in hot paths within titiler's factory routing system, particularly when:

  • Generating TileMatrixSet lists for OGC API endpoints (self.supported_tms.list())
  • Building API path parameters with Literal[tuple(self.supported_tms.list())]
  • Processing HTTP requests that need to enumerate available algorithms/tilesets

Test Results Analysis:
The optimization shows consistent improvements across all test cases:

  • Small datasets (1-3 items): 21-47% faster
  • Large datasets (1000 items): 4-5% faster (still meaningful at scale)
  • Edge cases with special characters/unicode: 24-36% faster

Since this method appears to be called frequently in web request processing pipelines, even small per-call improvements compound into significant overall performance gains.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 38 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 1 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
from typing import Dict, List, Type

import attr
# imports
import pytest  # used for our unit tests
from titiler.core.algorithm.__init__ import Algorithms

# Minimal stub to satisfy type requirements for BaseAlgorithm
class BaseAlgorithm:
    pass
from titiler.core.algorithm.__init__ import Algorithms

# unit tests

class DummyAlgorithm1(BaseAlgorithm):
    pass

class DummyAlgorithm2(BaseAlgorithm):
    pass

class DummyAlgorithm3(BaseAlgorithm):
    pass

def test_list_basic_single_entry():
    # Basic: One algorithm registered
    algos = Algorithms(data={"algo1": DummyAlgorithm1})
    codeflash_output = algos.list(); result = codeflash_output # 1.01μs -> 686ns (46.9% faster)

def test_list_basic_multiple_entries():
    # Basic: Multiple algorithms registered
    algos = Algorithms(data={"algo1": DummyAlgorithm1, "algo2": DummyAlgorithm2})
    codeflash_output = algos.list(); result = codeflash_output # 783ns -> 605ns (29.4% faster)

def test_list_basic_empty():
    # Basic: No algorithms registered
    algos = Algorithms(data={})
    codeflash_output = algos.list(); result = codeflash_output # 675ns -> 557ns (21.2% faster)

def test_list_edge_non_string_keys():
    # Edge: Non-string keys (should be strings per spec, but test what happens)
    algos = Algorithms(data={1: DummyAlgorithm1, "algo2": DummyAlgorithm2})
    codeflash_output = algos.list(); result = codeflash_output # 721ns -> 573ns (25.8% faster)

def test_list_edge_duplicate_keys():
    # Edge: Duplicate keys not possible in dict, but test overwriting
    algos = Algorithms(data={"algo1": DummyAlgorithm1, "algo1": DummyAlgorithm2})
    codeflash_output = algos.list(); result = codeflash_output # 687ns -> 555ns (23.8% faster)

def test_list_edge_long_key_names():
    # Edge: Very long algorithm names
    long_name = "a" * 500
    algos = Algorithms(data={long_name: DummyAlgorithm1})
    codeflash_output = algos.list(); result = codeflash_output # 685ns -> 551ns (24.3% faster)

def test_list_edge_special_characters_in_keys():
    # Edge: Algorithm names with special characters
    special_name = "algo!@#$%^&*()_+-=[]{}|;':,.<>/?"
    algos = Algorithms(data={special_name: DummyAlgorithm1})
    codeflash_output = algos.list(); result = codeflash_output # 663ns -> 491ns (35.0% faster)

def test_list_edge_unicode_keys():
    # Edge: Unicode algorithm names
    unicode_name = "алгоритм"
    algos = Algorithms(data={unicode_name: DummyAlgorithm1})
    codeflash_output = algos.list(); result = codeflash_output # 732ns -> 586ns (24.9% faster)

def test_list_large_scale_many_algorithms():
    # Large Scale: 1000 algorithms registered
    data = {f"algo_{i}": DummyAlgorithm1 for i in range(1000)}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 3.83μs -> 3.68μs (4.16% faster)
    # Check that all expected keys are present
    for i in range(1000):
        pass

def test_list_large_scale_performance():
    # Large Scale: Test performance for 1000 algorithms (not strict timing, but should not hang)
    data = {f"algo_{i}": DummyAlgorithm1 for i in range(1000)}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 3.83μs -> 3.69μs (3.88% faster)

def test_list_edge_empty_string_key():
    # Edge: Key is empty string
    algos = Algorithms(data={"": DummyAlgorithm1})
    codeflash_output = algos.list(); result = codeflash_output # 708ns -> 540ns (31.1% faster)

def test_list_edge_none_key():
    # Edge: Key is None (not recommended, but possible in Python dict)
    algos = Algorithms(data={None: DummyAlgorithm1})
    codeflash_output = algos.list(); result = codeflash_output # 671ns -> 550ns (22.0% faster)

def test_list_edge_mixed_key_types():
    # Edge: Mixed key types
    algos = Algorithms(data={"algo1": DummyAlgorithm1, 2: DummyAlgorithm2, (3,): DummyAlgorithm3})
    codeflash_output = algos.list(); result = codeflash_output # 677ns -> 465ns (45.6% faster)

def test_list_edge_mutation_safety():
    # Edge: Ensure that returned list is a copy and not affected by later mutations to the dict
    data = {"algo1": DummyAlgorithm1, "algo2": DummyAlgorithm2}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 713ns -> 536ns (33.0% faster)
    data["algo3"] = DummyAlgorithm3  # mutate original dict

def test_list_edge_key_order():
    # Edge: Dict key order should be preserved (Python 3.7+)
    keys = [f"algo_{i}" for i in range(10)]
    data = {k: DummyAlgorithm1 for k in keys}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 692ns -> 585ns (18.3% faster)

def test_list_edge_algorithm_type_is_subclass():
    # Edge: Algorithm type is a subclass of BaseAlgorithm
    class SubAlgorithm(DummyAlgorithm1):
        pass
    algos = Algorithms(data={"sub_algo": SubAlgorithm})
    codeflash_output = algos.list(); result = codeflash_output # 699ns -> 555ns (25.9% faster)

def test_list_edge_algorithm_type_is_not_subclass():
    # Edge: Algorithm type is not a subclass of BaseAlgorithm (should still list key)
    class NotAnAlgorithm:
        pass
    algos = Algorithms(data={"not_algo": NotAnAlgorithm})
    codeflash_output = algos.list(); result = codeflash_output # 686ns -> 524ns (30.9% faster)

def test_list_edge_algorithm_type_is_none():
    # Edge: Algorithm type is None
    algos = Algorithms(data={"none_algo": None})
    codeflash_output = algos.list(); result = codeflash_output # 637ns -> 487ns (30.8% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
from typing import Dict, List, Type

# function to test
import attr
# imports
import pytest  # used for our unit tests
from titiler.core.algorithm.__init__ import Algorithms

# Dummy BaseAlgorithm for testing
class BaseAlgorithm:
    pass
from titiler.core.algorithm.__init__ import Algorithms

# unit tests

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

def test_list_returns_empty_list_when_no_algorithms():
    # Test with no algorithms registered
    algos = Algorithms(data={})
    codeflash_output = algos.list() # 666ns -> 497ns (34.0% faster)

def test_list_returns_single_algorithm():
    # Test with one algorithm registered
    class Algo1(BaseAlgorithm): pass
    algos = Algorithms(data={"algo1": Algo1})
    codeflash_output = algos.list() # 702ns -> 541ns (29.8% faster)

def test_list_returns_multiple_algorithms():
    # Test with multiple algorithms registered
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    class Algo3(BaseAlgorithm): pass
    algos = Algorithms(data={"a1": Algo1, "a2": Algo2, "a3": Algo3})
    codeflash_output = algos.list(); result = codeflash_output # 729ns -> 553ns (31.8% faster)

def test_list_returns_exact_keys():
    # Test that the returned list matches exactly the keys of the data dict
    class AlgoA(BaseAlgorithm): pass
    class AlgoB(BaseAlgorithm): pass
    keys = ["foo", "bar"]
    algos = Algorithms(data={k: AlgoA if i == 0 else AlgoB for i, k in enumerate(keys)})
    codeflash_output = algos.list() # 709ns -> 526ns (34.8% faster)

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

def test_list_with_non_string_keys():
    # Test with non-string keys (should still work, but returns as-is)
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    algos = Algorithms(data={42: Algo1, None: Algo2})
    codeflash_output = algos.list(); result = codeflash_output # 683ns -> 503ns (35.8% faster)

def test_list_with_empty_string_key():
    # Test with empty string as key
    class Algo1(BaseAlgorithm): pass
    algos = Algorithms(data={"": Algo1})
    codeflash_output = algos.list() # 705ns -> 528ns (33.5% faster)

def test_list_with_duplicate_keys():
    # Test with duplicate keys (not possible in dict, but check that only one appears)
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    # Only last value for duplicate key remains
    algos = Algorithms(data={"dup": Algo1, "dup": Algo2})
    codeflash_output = algos.list(); result = codeflash_output # 700ns -> 577ns (21.3% faster)

def test_list_with_special_character_keys():
    # Test with keys containing special characters
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    special_keys = ["algo$", "algo space", "algo-unicode-ß"]
    algos = Algorithms(data={k: Algo1 for k in special_keys})
    codeflash_output = algos.list() # 774ns -> 578ns (33.9% faster)

def test_list_with_long_key_names():
    # Test with very long string keys
    class Algo1(BaseAlgorithm): pass
    long_key = "a" * 1000
    algos = Algorithms(data={long_key: Algo1})
    codeflash_output = algos.list() # 717ns -> 525ns (36.6% faster)

def test_list_with_various_types_of_keys():
    # Test with mixed key types
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    class Algo3(BaseAlgorithm): pass
    keys = [1, "two", (3, "three")]
    algos = Algorithms(data={keys[0]: Algo1, keys[1]: Algo2, keys[2]: Algo3})
    codeflash_output = algos.list(); result = codeflash_output # 727ns -> 576ns (26.2% faster)

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

def test_list_with_1000_algorithms():
    # Test with 1000 algorithms registered
    class Algo(BaseAlgorithm): pass
    keys = [f"algo_{i}" for i in range(1000)]
    data = {key: Algo for key in keys}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 3.90μs -> 3.75μs (4.02% faster)

def test_list_performance_large_dict():
    # Test performance with large data (not strict timing, but should not hang)
    class Algo(BaseAlgorithm): pass
    keys = [f"alg_{i}" for i in range(999)]
    data = {key: Algo for key in keys}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 3.84μs -> 3.65μs (5.23% faster)

def test_list_with_large_non_string_keys():
    # Test with large number of non-string keys
    class Algo(BaseAlgorithm): pass
    codeflash_output = list(range(1000)); keys = codeflash_output # 5.56μs -> 5.42μs (2.58% faster)
    data = {k: Algo for k in keys}
    algos = Algorithms(data=data)
    codeflash_output = algos.list(); result = codeflash_output # 3.72μs -> 3.55μs (4.76% faster)

# ---- Determinism and Mutation Testing ----

def test_list_does_not_return_values():
    # Test that list() does NOT return values, only keys
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    algos = Algorithms(data={"a": Algo1, "b": Algo2})
    codeflash_output = algos.list(); result = codeflash_output # 686ns -> 546ns (25.6% faster)

def test_list_is_not_mutable():
    # Test that returned list is not a reference to internal data
    class Algo1(BaseAlgorithm): pass
    algos = Algorithms(data={"x": Algo1})
    codeflash_output = algos.list(); out = codeflash_output # 700ns -> 534ns (31.1% faster)
    out.append("y")
    # Internal data should not be affected
    codeflash_output = algos.list() # 319ns -> 267ns (19.5% faster)

def test_list_order_is_consistent():
    # Test that order is consistent across calls
    class Algo1(BaseAlgorithm): pass
    class Algo2(BaseAlgorithm): pass
    keys = ["first", "second"]
    algos = Algorithms(data={k: Algo1 if i == 0 else Algo2 for i, k in enumerate(keys)})
    codeflash_output = algos.list() # 696ns -> 514ns (35.4% faster)
    codeflash_output = algos.list() # 305ns -> 242ns (26.0% faster)

# ---- Error Handling (if applicable) ----

def test_list_with_missing_data_attribute_raises():
    # Test that missing 'data' attribute raises error
    with pytest.raises(TypeError):
        Algorithms()
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
from titiler.core.algorithm.__init__ import Algorithms

def test_Algorithms_list():
    Algorithms.list(Algorithms({}))
🔎 Concolic Coverage Tests and Runtime

To edit these changes git checkout codeflash/optimize-Algorithms.list-mi8djae6 and push.

Codeflash Static Badge

The optimization replaces `list(self.data.keys())` with `[*self.data]` (unpacking operator), achieving a **14% speedup** by eliminating the intermediate `.keys()` view object creation.

**Key Changes:**
- **Direct unpacking**: `[*self.data]` directly unpacks dictionary keys into a list
- **Eliminated intermediate step**: Removes the `.keys()` method call and `list()` constructor overhead
- **Added explicit attribute definition**: Added `data = attr.ib()` for proper attrs compatibility

**Why It's Faster:**
In Python, `dict.keys()` creates a dictionary view object that must then be converted to a list. The unpacking operator `[*self.data]` bypasses this intermediate step by directly iterating over the dictionary keys during list construction, reducing function call overhead and object creation.

**Performance Context:**
Based on function_references, this method is called in hot paths within titiler's factory routing system, particularly when:
- Generating TileMatrixSet lists for OGC API endpoints (`self.supported_tms.list()`)
- Building API path parameters with `Literal[tuple(self.supported_tms.list())]`
- Processing HTTP requests that need to enumerate available algorithms/tilesets

**Test Results Analysis:**
The optimization shows consistent improvements across all test cases:
- Small datasets (1-3 items): 21-47% faster
- Large datasets (1000 items): 4-5% faster (still meaningful at scale)
- Edge cases with special characters/unicode: 24-36% faster

Since this method appears to be called frequently in web request processing pipelines, even small per-call improvements compound into significant overall performance gains.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 21, 2025 04:42
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Nov 21, 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