Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 288% (2.88x) speedup for Perspective._get_theme in panel/pane/perspective.py

⏱️ Runtime : 4.15 milliseconds 1.07 milliseconds (best of 41 runs)

📝 Explanation and details

The optimization achieves a 288% speedup by eliminating a critical performance bottleneck: the repeated import of THEME_URL inside the _get_theme method.

Key optimizations applied:

  1. Module-level import caching: Moved from panel.models.perspective import THEME_URL from inside the method to module scope. The line profiler shows this import was consuming 61.4% of total runtime (6.48ms out of 10.56ms total) across 4157 calls. By importing once at module load instead of on every method call, this overhead is completely eliminated.

  2. Reduced attribute lookups: Cached self._bokeh_model in a local variable bokeh_model and bokeh_model.__css_raw__ in css_raw to avoid repeated attribute access during the conditional block.

Why this leads to speedup:

  • Python's import mechanism involves module lookup, attribute resolution, and potential disk I/O on first import
  • Repeated imports of the same module/attribute create unnecessary overhead in the method's hot path
  • Local variable access is faster than repeated attribute lookups through object hierarchies

Test case performance:
The optimization is particularly effective for:

  • Frequent theme switching scenarios (317% faster on 1000 unique themes)
  • Material theme replacements (275% faster on 1000 material themes)
  • Basic theme operations (220-290% faster across all standard themes)
  • Long theme strings (146% faster on 1000-character themes)

The optimization maintains identical behavior while dramatically reducing the computational cost of what was previously a simple but expensive operation.

Correctness verification report:

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

# --- Function to test ---
# Minimal mock for THEME_URL and Perspective class to allow testing of _get_theme.

THEME_URL = "https://cdn.perspective.com/themes/"

class MockBokehModel:
    def __init__(self):
        self.__css_raw__ = ['a', 'b', 'c', 'd', 'e']
from panel.pane.perspective import Perspective

# --- Unit Tests ---

# Basic Test Cases

def test_basic_theme_pro():
    """Test basic theme 'pro' returns correct URL."""
    p = Perspective()
    codeflash_output = p._get_theme('pro') # 3.86μs -> 1.09μs (255% faster)

def test_basic_theme_pro_dark():
    """Test basic theme 'pro-dark' returns correct URL."""
    p = Perspective()
    codeflash_output = p._get_theme('pro-dark') # 4.01μs -> 1.20μs (233% faster)

def test_basic_theme_monokai():
    """Test basic theme 'monokai' returns correct URL, with no replacement."""
    p = Perspective()
    codeflash_output = p._get_theme('monokai') # 3.90μs -> 1.05μs (273% faster)

def test_basic_theme_solarized():
    """Test basic theme 'solarized' returns correct URL."""
    p = Perspective()
    codeflash_output = p._get_theme('solarized') # 3.75μs -> 1.17μs (220% faster)

def test_basic_theme_solarized_dark():
    """Test basic theme 'solarized-dark' returns correct URL."""
    p = Perspective()
    codeflash_output = p._get_theme('solarized-dark') # 3.88μs -> 1.23μs (216% faster)

def test_basic_theme_vaporwave():
    """Test basic theme 'vaporwave' returns correct URL."""
    p = Perspective()
    codeflash_output = p._get_theme('vaporwave') # 3.88μs -> 1.22μs (218% faster)

def test_basic_theme_material():
    """Test 'material' is replaced with 'pro' in the URL."""
    p = Perspective()
    codeflash_output = p._get_theme('material') # 4.29μs -> 1.46μs (194% faster)

def test_basic_theme_material_dark():
    """Test 'material-dark' is replaced with 'pro-dark' in the URL."""
    p = Perspective()
    codeflash_output = p._get_theme('material-dark') # 4.35μs -> 1.68μs (159% faster)

# Edge Test Cases

def test_edge_theme_with_material_substring():
    """Test theme with 'material' substring in the middle is replaced."""
    p = Perspective()
    theme = 'foo-material-bar'
    expected = f"{THEME_URL}foo-pro-bar.css"
    codeflash_output = p._get_theme(theme) # 4.42μs -> 1.63μs (171% faster)

def test_edge_theme_with_multiple_material():
    """Test theme with multiple 'material' substrings are all replaced."""
    p = Perspective()
    theme = 'material-material-dark'
    expected = f"{THEME_URL}pro-pro-dark.css"
    codeflash_output = p._get_theme(theme) # 4.44μs -> 1.75μs (154% faster)

def test_edge_theme_empty_string():
    """Test empty theme string returns correct URL."""
    p = Perspective()
    expected = f"{THEME_URL}.css"
    codeflash_output = p._get_theme('') # 3.79μs -> 1.10μs (243% faster)

def test_edge_theme_special_characters():
    """Test theme with special characters."""
    p = Perspective()
    theme = 'my_theme@42'
    expected = f"{THEME_URL}my_theme@42.css"
    codeflash_output = p._get_theme(theme) # 3.90μs -> 1.16μs (237% faster)

def test_edge_theme_numeric():
    """Test theme with numeric string."""
    p = Perspective()
    theme = '123456'
    expected = f"{THEME_URL}123456.css"
    codeflash_output = p._get_theme(theme) # 3.77μs -> 1.05μs (260% faster)

def test_edge_theme_none_type():
    """Test passing None as theme raises AttributeError (since None has no replace)."""
    p = Perspective()
    with pytest.raises(AttributeError):
        p._get_theme(None) # 3.99μs -> 1.44μs (178% faster)

def test_edge_theme_non_string_type():
    """Test passing an integer as theme raises AttributeError."""
    p = Perspective()
    with pytest.raises(AttributeError):
        p._get_theme(123) # 3.87μs -> 1.42μs (173% faster)

def test_edge_theme_with_spaces():
    """Test theme with spaces is handled correctly."""
    p = Perspective()
    theme = 'pro dark'
    expected = f"{THEME_URL}pro dark.css"
    codeflash_output = p._get_theme(theme) # 3.98μs -> 1.14μs (249% faster)

def test_edge_theme_long_string():
    """Test very long theme string."""
    p = Perspective()
    theme = 'material' * 50
    expected = f"{THEME_URL}{'pro'*50}.css"
    codeflash_output = p._get_theme(theme) # 5.25μs -> 2.37μs (121% faster)

def test_edge_theme_url_injection():
    """Test theme with URL-like string."""
    p = Perspective()
    theme = 'http://evil.com/theme'
    expected = f"{THEME_URL}http://evil.com/theme.css"
    codeflash_output = p._get_theme(theme) # 3.87μs -> 1.17μs (232% faster)

# Test with _bokeh_model present

def test_bokeh_model_css_raw_updated():
    """Test that _bokeh_model.__css_raw__ is updated correctly."""
    p = Perspective()
    p._bokeh_model = MockBokehModel()
    theme = 'pro-dark'
    codeflash_output = p._get_theme(theme); url = codeflash_output # 4.46μs -> 1.66μs (169% faster)

def test_bokeh_model_css_raw_multiple_calls():
    """Test that multiple calls append multiple theme URLs."""
    p = Perspective()
    p._bokeh_model = MockBokehModel()
    codeflash_output = p._get_theme('pro'); url1 = codeflash_output # 4.51μs -> 1.67μs (171% faster)
    codeflash_output = p._get_theme('monokai'); url2 = codeflash_output # 1.68μs -> 592ns (184% faster)

def test_bokeh_model_none():
    """Test that nothing breaks if _bokeh_model is None."""
    p = Perspective()
    p._bokeh_model = None
    codeflash_output = p._get_theme('pro'); url = codeflash_output # 3.68μs -> 945ns (289% faster)

# Large Scale Test Cases

def test_large_scale_many_themes():
    """Test with a large number of unique theme names."""
    p = Perspective()
    for i in range(1000):
        theme = f"theme_{i}"
        expected = f"{THEME_URL}theme_{i}.css"
        codeflash_output = p._get_theme(theme) # 929μs -> 222μs (317% faster)

def test_large_scale_material_replacement():
    """Test with a large number of themes containing 'material'."""
    p = Perspective()
    for i in range(1000):
        theme = f"foo_material_{i}"
        expected = f"{THEME_URL}foo_pro_{i}.css"
        codeflash_output = p._get_theme(theme) # 976μs -> 260μs (275% faster)

def test_large_scale_bokeh_model_append():
    """Test _bokeh_model.__css_raw__ for many theme changes."""
    p = Perspective()
    p._bokeh_model = MockBokehModel()
    for i in range(10):  # Keep this reasonable for speed
        theme = f"theme_{i}"
        codeflash_output = p._get_theme(theme); url = codeflash_output # 14.9μs -> 4.78μs (211% faster)

def test_large_scale_long_theme_string():
    """Test with a very long theme string (length ~1000)."""
    p = Perspective()
    theme = 'a' * 1000
    expected = f"{THEME_URL}{'a'*1000}.css"
    codeflash_output = p._get_theme(theme) # 4.70μs -> 1.91μs (146% faster)

def test_large_scale_long_material_theme_string():
    """Test with a very long theme string containing 'material'."""
    p = Perspective()
    theme = 'material' * 100
    expected = f"{THEME_URL}{'pro'*100}.css"
    codeflash_output = p._get_theme(theme) # 5.95μs -> 3.15μs (88.9% 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 panel.pane.perspective import Perspective

# --- Function to test: _get_theme ---
# Minimal definition of THEME_URL and Perspective class for testing
THEME_URL = "https://cdn.example.com/themes/"

class DummyBokehModel:
    def __init__(self):
        # Simulate a __css_raw__ attribute as a list
        self.__css_raw__ = ["a", "b", "c", "d", "e"]
from panel.pane.perspective import Perspective

# --- Unit Tests for _get_theme ---

# 1. Basic Test Cases
def test_get_theme_basic_pro():
    # Test with 'pro' theme
    p = Perspective()
    codeflash_output = p._get_theme('pro'); url = codeflash_output # 3.88μs -> 1.05μs (270% faster)

def test_get_theme_basic_pro_dark():
    # Test with 'pro-dark' theme
    p = Perspective()
    codeflash_output = p._get_theme('pro-dark'); url = codeflash_output # 3.87μs -> 1.11μs (250% faster)

def test_get_theme_basic_monokai():
    # Test with 'monokai' theme
    p = Perspective()
    codeflash_output = p._get_theme('monokai'); url = codeflash_output # 3.82μs -> 1.04μs (267% faster)

def test_get_theme_basic_material_replacement():
    # Test that 'material' is replaced with 'pro'
    p = Perspective()
    codeflash_output = p._get_theme('material'); url = codeflash_output # 4.28μs -> 1.42μs (201% faster)

def test_get_theme_basic_material_dark_replacement():
    # Test that 'material-dark' is replaced with 'pro-dark'
    p = Perspective()
    codeflash_output = p._get_theme('material-dark'); url = codeflash_output # 4.29μs -> 1.49μs (188% faster)

def test_get_theme_basic_solarized():
    # Test with 'solarized' theme
    p = Perspective()
    codeflash_output = p._get_theme('solarized'); url = codeflash_output # 3.88μs -> 1.22μs (219% faster)

def test_get_theme_basic_solarized_dark():
    # Test with 'solarized-dark' theme
    p = Perspective()
    codeflash_output = p._get_theme('solarized-dark'); url = codeflash_output # 3.90μs -> 1.15μs (239% faster)

def test_get_theme_basic_vaporwave():
    # Test with 'vaporwave' theme
    p = Perspective()
    codeflash_output = p._get_theme('vaporwave'); url = codeflash_output # 3.87μs -> 1.19μs (226% faster)

# 2. Edge Test Cases

def test_get_theme_empty_string():
    # Test with empty string as theme
    p = Perspective()
    codeflash_output = p._get_theme(''); url = codeflash_output # 3.95μs -> 1.02μs (287% faster)

def test_get_theme_material_substring():
    # Test with a theme containing 'material' as a substring
    p = Perspective()
    codeflash_output = p._get_theme('materialized'); url = codeflash_output # 4.26μs -> 1.55μs (175% faster)

def test_get_theme_multiple_materials():
    # Test with multiple 'material' substrings
    p = Perspective()
    codeflash_output = p._get_theme('material-material-dark'); url = codeflash_output # 4.53μs -> 1.69μs (169% faster)

def test_get_theme_special_characters():
    # Test with special characters in theme
    p = Perspective()
    codeflash_output = p._get_theme('solarized@2024!'); url = codeflash_output # 3.90μs -> 1.15μs (238% faster)

def test_get_theme_numeric_theme():
    # Test with a numeric theme name
    p = Perspective()
    codeflash_output = p._get_theme('12345'); url = codeflash_output # 3.65μs -> 1.04μs (251% faster)

def test_get_theme_none_theme():
    # Test with None as theme (should raise AttributeError)
    p = Perspective()
    with pytest.raises(AttributeError):
        p._get_theme(None) # 3.93μs -> 1.42μs (177% faster)

def test_get_theme_bokeh_model_update():
    # Test that __css_raw__ is updated when _bokeh_model is set
    p = Perspective()
    model = DummyBokehModel()
    p._bokeh_model = model
    theme_name = 'pro-dark'
    codeflash_output = p._get_theme(theme_name); url = codeflash_output # 4.64μs -> 1.75μs (166% faster)

def test_get_theme_bokeh_model_material_replacement():
    # Test that 'material' is replaced in the theme url and in __css_raw__
    p = Perspective()
    model = DummyBokehModel()
    p._bokeh_model = model
    codeflash_output = p._get_theme('material'); url = codeflash_output # 4.93μs -> 1.93μs (155% faster)

def test_get_theme_bokeh_model_empty_css_raw():
    # Test with empty __css_raw__ list
    p = Perspective()
    model = DummyBokehModel()
    model.__css_raw__ = []
    p._bokeh_model = model
    codeflash_output = p._get_theme('pro'); url = codeflash_output # 4.37μs -> 1.48μs (196% faster)

def test_get_theme_bokeh_model_none_css_raw():
    # Test with __css_raw__ set to None (should raise TypeError)
    p = Perspective()
    model = DummyBokehModel()
    model.__css_raw__ = None
    p._bokeh_model = model
    with pytest.raises(TypeError):
        p._get_theme('pro') # 4.88μs -> 1.88μs (159% faster)

def test_get_theme_bokeh_model_short_css_raw():
    # Test with __css_raw__ shorter than 5 elements
    p = Perspective()
    model = DummyBokehModel()
    model.__css_raw__ = ["x", "y"]
    p._bokeh_model = model
    codeflash_output = p._get_theme('pro'); url = codeflash_output # 4.47μs -> 1.48μs (203% faster)

def test_get_theme_resources_argument_unused():
    # Test that resources argument does not affect output
    p = Perspective()
    codeflash_output = p._get_theme('pro'); url1 = codeflash_output # 3.72μs -> 1.04μs (258% faster)
    codeflash_output = p._get_theme('pro', resources="some_resource"); url2 = codeflash_output # 1.73μs -> 636ns (173% faster)

# 3. Large Scale Test Cases

def test_get_theme_large_scale_unique_themes():
    # Test with a large number of unique theme names
    p = Perspective()
    for i in range(1000):
        theme_name = f"theme_{i}"
        codeflash_output = p._get_theme(theme_name); url = codeflash_output # 938μs -> 222μs (322% faster)

def test_get_theme_large_scale_material_replacement():
    # Test with many theme names containing 'material'
    p = Perspective()
    for i in range(1000):
        theme_name = f"material_theme_{i}"
        codeflash_output = p._get_theme(theme_name); url = codeflash_output # 977μs -> 256μs (281% faster)

def test_get_theme_large_scale_bokeh_model():
    # Test with many calls updating __css_raw__ on a bokeh model
    p = Perspective()
    model = DummyBokehModel()
    p._bokeh_model = model
    for i in range(100):
        theme_name = f"pro_{i}"
        codeflash_output = p._get_theme(theme_name); url = codeflash_output # 119μs -> 35.9μs (232% faster)

def test_get_theme_large_scale_long_theme_name():
    # Test with a very long theme name
    p = Perspective()
    long_theme = "x" * 500
    codeflash_output = p._get_theme(long_theme); url = codeflash_output # 4.10μs -> 1.38μs (197% faster)

def test_get_theme_large_scale_multiple_materials():
    # Test with a theme name containing 'material' many times
    p = Perspective()
    theme_name = "material-" * 50 + "dark"
    codeflash_output = p._get_theme(theme_name); url = codeflash_output # 5.33μs -> 2.61μs (104% faster)
    expected = "pro-" * 50 + "dark"
# 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-Perspective._get_theme-mhbl4e1q and push.

Codeflash

The optimization achieves a **288% speedup** by eliminating a critical performance bottleneck: the repeated import of `THEME_URL` inside the `_get_theme` method.

**Key optimizations applied:**

1. **Module-level import caching**: Moved `from panel.models.perspective import THEME_URL` from inside the method to module scope. The line profiler shows this import was consuming **61.4% of total runtime** (6.48ms out of 10.56ms total) across 4157 calls. By importing once at module load instead of on every method call, this overhead is completely eliminated.

2. **Reduced attribute lookups**: Cached `self._bokeh_model` in a local variable `bokeh_model` and `bokeh_model.__css_raw__` in `css_raw` to avoid repeated attribute access during the conditional block.

**Why this leads to speedup:**
- Python's import mechanism involves module lookup, attribute resolution, and potential disk I/O on first import
- Repeated imports of the same module/attribute create unnecessary overhead in the method's hot path
- Local variable access is faster than repeated attribute lookups through object hierarchies

**Test case performance:**
The optimization is particularly effective for:
- **Frequent theme switching scenarios** (317% faster on 1000 unique themes)
- **Material theme replacements** (275% faster on 1000 material themes) 
- **Basic theme operations** (220-290% faster across all standard themes)
- **Long theme strings** (146% faster on 1000-character themes)

The optimization maintains identical behavior while dramatically reducing the computational cost of what was previously a simple but expensive operation.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 29, 2025 05:58
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 29, 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