Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 95% (0.95x) speedup for HoloViews._resolve_widget in panel/pane/holoviews.py

⏱️ Runtime : 583 microseconds 298 microseconds (best of 28 runs)

📝 Explanation and details

The optimization restructures the widget lookup logic in the _resolve_widget method to avoid expensive redundant dictionary operations.

Key change: Instead of using a chained .get() call default_widgets.get(key, cls.default_widgets.get(key, None)), the code now uses an explicit conditional check: first verifying if default_widgets is not None and contains the key, then falling back to the class default.

Why this is faster: The original chained approach always executed both dictionary lookups - first on default_widgets and then on cls.default_widgets.get(key, None) as the fallback value, even when the key was found in the first dictionary. The optimized version performs only one lookup when the key exists in default_widgets (the common case based on the profiler showing 1032 hits vs 11 misses).

Performance impact: Line profiler shows the critical lookup line dropped from 1.76ms (66.9% of total time) to distributed across two much faster operations (24.2% and 3.2%), achieving a 95% speedup overall.

Test case benefits: The optimization shows dramatic improvements when custom default_widgets mappings are provided (99.5% faster in some cases), making it particularly effective for applications that frequently override default widget configurations or use large custom widget mappings.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 1039 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest
from panel.pane.holoviews import HoloViews


# Dummy WidgetBase classes for testing
class WidgetBase: pass
class WidgetA(WidgetBase): pass
class WidgetB(WidgetBase): pass
class WidgetC(WidgetBase): pass

# The function to test, adapted and isolated for unit testing
def _resolve_widget(key: str, dynamic: bool, default_widgets: dict = None):
    """
    Resolves the widget type for a given key and dynamic/static context.
    - key: The type of widget to resolve (e.g., 'float', 'int', etc.)
    - dynamic: If True, selects the dynamic widget (second in tuple); else static (first in tuple).
    - default_widgets: Optional dict mapping keys to widget types or (static, dynamic) tuples.
    """
    # Simulate class-level default_widgets
    class_default_widgets = {
        'date': WidgetA,
        'discrete': WidgetB,
        'discrete_numeric': WidgetC,
        'float': (WidgetA, WidgetB),
        'int': (WidgetB, WidgetC),
        'scrubber': WidgetC
    }
    if default_widgets is None:
        default_widgets = {}
    widget_type = default_widgets.get(key, class_default_widgets.get(key, None))
    if widget_type is None:
        raise ValueError(f"No valid {key} widget type found.")
    elif isinstance(widget_type, tuple):
        widget_type = widget_type[int(dynamic)]
    return widget_type

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


























#------------------------------------------------
import pytest
from panel.pane.holoviews import HoloViews


# Minimal widget classes to simulate Panel widgets for testing
class WidgetBase:
    pass

class DatetimeInput(WidgetBase):
    pass

class Select(WidgetBase):
    pass

class DiscreteSlider(WidgetBase):
    pass

class EditableFloatSlider(WidgetBase):
    pass

class EditableIntSlider(WidgetBase):
    pass

class FloatSlider(WidgetBase):
    pass

class IntSlider(WidgetBase):
    pass

class Player(WidgetBase):
    pass
from panel.pane.holoviews import HoloViews

# ---------------- BASIC TEST CASES ----------------

def test_resolve_widget_basic_date():
    # Should resolve to DatetimeInput for 'date' key
    codeflash_output = HoloViews._resolve_widget('date', False) # 2.88μs -> 2.76μs (4.20% faster)

def test_resolve_widget_basic_discrete():
    # Should resolve to Select for 'discrete' key
    codeflash_output = HoloViews._resolve_widget('discrete', False) # 2.22μs -> 2.03μs (9.57% faster)

def test_resolve_widget_basic_discrete_numeric():
    # Should resolve to DiscreteSlider for 'discrete_numeric' key
    codeflash_output = HoloViews._resolve_widget('discrete_numeric', False) # 2.11μs -> 1.92μs (10.1% faster)

def test_resolve_widget_basic_scrubber():
    # Should resolve to Player for 'scrubber' key
    codeflash_output = HoloViews._resolve_widget('scrubber', False) # 2.16μs -> 1.94μs (11.7% faster)

def test_resolve_widget_basic_float_static():
    # Should resolve to FloatSlider for 'float', dynamic=False (static)
    codeflash_output = HoloViews._resolve_widget('float', False) # 2.20μs -> 2.23μs (1.21% slower)

def test_resolve_widget_basic_float_dynamic():
    # Should resolve to EditableFloatSlider for 'float', dynamic=True (dynamic)
    codeflash_output = HoloViews._resolve_widget('float', True) # 2.08μs -> 2.05μs (1.41% faster)

def test_resolve_widget_basic_int_static():
    # Should resolve to IntSlider for 'int', dynamic=False (static)
    codeflash_output = HoloViews._resolve_widget('int', False) # 2.21μs -> 2.14μs (3.28% faster)

def test_resolve_widget_basic_int_dynamic():
    # Should resolve to EditableIntSlider for 'int', dynamic=True (dynamic)
    codeflash_output = HoloViews._resolve_widget('int', True) # 2.09μs -> 1.95μs (6.81% faster)

# ---------------- EDGE TEST CASES ----------------


def test_resolve_widget_custom_default_widgets_overrides_class():
    # Should use provided default_widgets mapping over class default_widgets
    class CustomWidget(WidgetBase): pass
    custom_map = {'date': CustomWidget}
    codeflash_output = HoloViews._resolve_widget('date', False, default_widgets=custom_map) # 2.58μs -> 1.29μs (99.5% faster)

def test_resolve_widget_custom_tuple_in_default_widgets():
    # Should handle tuple in provided default_widgets mapping
    class W1(WidgetBase): pass
    class W2(WidgetBase): pass
    custom_map = {'float': (W1, W2)}
    codeflash_output = HoloViews._resolve_widget('float', False, default_widgets=custom_map) # 2.52μs -> 1.33μs (88.8% faster)
    codeflash_output = HoloViews._resolve_widget('float', True, default_widgets=custom_map) # 924ns -> 499ns (85.2% faster)

def test_resolve_widget_tuple_dynamic_out_of_bounds():
    # Should raise IndexError if dynamic is not 0 or 1 for tuple mapping
    class W1(WidgetBase): pass
    class W2(WidgetBase): pass
    custom_map = {'float': (W1, W2)}
    # Only dynamic=True/False (0/1) is supported; test with dynamic=2
    with pytest.raises(IndexError):
        HoloViews._resolve_widget('float', 2, default_widgets=custom_map) # 2.57μs -> 1.49μs (71.9% faster)


def test_resolve_widget_tuple_with_non_bool_dynamic():
    # Should treat dynamic as bool/int and index the tuple accordingly
    # dynamic='1' should be interpreted as int(1) -> second tuple element
    class W1(WidgetBase): pass
    class W2(WidgetBase): pass
    custom_map = {'float': (W1, W2)}
    codeflash_output = HoloViews._resolve_widget('float', 1, default_widgets=custom_map) # 2.56μs -> 1.33μs (92.6% faster)
    codeflash_output = HoloViews._resolve_widget('float', 0, default_widgets=custom_map) # 839ns -> 396ns (112% faster)

def test_resolve_widget_tuple_with_bool_dynamic():
    # Should work with dynamic as True/False
    class W1(WidgetBase): pass
    class W2(WidgetBase): pass
    custom_map = {'float': (W1, W2)}
    codeflash_output = HoloViews._resolve_widget('float', True, default_widgets=custom_map) # 2.31μs -> 1.24μs (85.5% faster)
    codeflash_output = HoloViews._resolve_widget('float', False, default_widgets=custom_map) # 836ns -> 456ns (83.3% faster)


def test_resolve_widget_large_default_widgets():
    # Should handle a large custom mapping without performance issues
    # Create 1000 unique widget classes and mappings
    custom_map = {}
    widget_classes = []
    for i in range(1000):
        cls = type(f'Widget{i}', (WidgetBase,), {})
        widget_classes.append(cls)
        custom_map[f'key{i}'] = cls
    # Test resolution for several keys
    for idx in [0, 499, 999]:
        key = f'key{idx}'
        codeflash_output = HoloViews._resolve_widget(key, False, default_widgets=custom_map) # 5.87μs -> 2.82μs (108% faster)

def test_resolve_widget_large_tuple_default_widgets():
    # Should handle a large tuple mapping
    for i in range(10):  # 10 keys, each with tuple of 2 widgets
        W1 = type(f'W{i}A', (WidgetBase,), {})
        W2 = type(f'W{i}B', (WidgetBase,), {})
        custom_map = {f'float{i}': (W1, W2)}
        codeflash_output = HoloViews._resolve_widget(f'float{i}', False, default_widgets=custom_map) # 8.35μs -> 4.40μs (89.8% faster)
        codeflash_output = HoloViews._resolve_widget(f'float{i}', True, default_widgets=custom_map)


def test_resolve_widget_performance_large_scale():
    # Ensure function is performant for large mapping (under 1000 elements)
    import time
    custom_map = {f'key{i}': WidgetBase for i in range(1000)}
    start = time.time()
    # Test 1000 lookups
    for i in range(1000):
        codeflash_output = HoloViews._resolve_widget(f'key{i}', False, default_widgets=custom_map) # 529μs -> 262μs (101% faster)
    duration = 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-HoloViews._resolve_widget-mha6w59y and push.

Codeflash

The optimization restructures the widget lookup logic in the `_resolve_widget` method to avoid expensive redundant dictionary operations.

**Key change:** Instead of using a chained `.get()` call `default_widgets.get(key, cls.default_widgets.get(key, None))`, the code now uses an explicit conditional check: first verifying if `default_widgets` is not None and contains the key, then falling back to the class default.

**Why this is faster:** The original chained approach always executed both dictionary lookups - first on `default_widgets` and then on `cls.default_widgets.get(key, None)` as the fallback value, even when the key was found in the first dictionary. The optimized version performs only one lookup when the key exists in `default_widgets` (the common case based on the profiler showing 1032 hits vs 11 misses).

**Performance impact:** Line profiler shows the critical lookup line dropped from 1.76ms (66.9% of total time) to distributed across two much faster operations (24.2% and 3.2%), achieving a 95% speedup overall.

**Test case benefits:** The optimization shows dramatic improvements when custom `default_widgets` mappings are provided (99.5% faster in some cases), making it particularly effective for applications that frequently override default widget configurations or use large custom widget mappings.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 28, 2025 06:32
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 28, 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