Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 33% (0.33x) speedup for bokeh_repr in panel/io/model.py

⏱️ Runtime : 419 microseconds 316 microseconds (best of 113 runs)

📝 Explanation and details

The optimization achieves a 32% speedup by eliminating repeated import overhead through import caching.

Key optimization:

  • Import caching: The original code imports Viewable on every function call (line profiler shows 194,845ns spent on import across 40 calls). The optimized version caches the import using function attributes - importing only once and storing Viewable as bokeh_repr._viewable for subsequent calls.

Performance impact:
The line profiler clearly shows the difference:

  • Original: 194,845ns (14.7% of total time) spent on from ..viewable import Viewable across 40 calls
  • Optimized: Only 6,959ns + 565ns (0.7% of total time) for the one-time import and caching

Why this works:
Python imports involve module lookup, loading, and namespace operations. By caching the imported class as a function attribute, we avoid this overhead on subsequent calls while maintaining the same functionality.

Test case benefits:
The optimization is particularly effective for:

  • Small-to-medium models (8-15μs → 4-6μs, ~50-60% faster): The import overhead was a significant portion of total execution time
  • Large-scale operations show smaller relative gains (2-4% faster) since property processing dominates execution time, making import overhead less significant

The caching approach preserves all original behavior while dramatically reducing the most expensive operation in the function's hot path.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 37 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 76.9%
🌀 Generated Regression Tests and Runtime
import textwrap

# imports
import pytest  # used for our unit tests
from panel.io.model import bokeh_repr


# Minimal stubs to enable testing of bokeh_repr
class Model:
    def __init__(self, **kwargs):
        self._props = kwargs.copy()
    def properties_with_values(self, include_defaults=False):
        # Return all properties except those set to None
        return {k: v for k, v in self._props.items() if v is not None}
    # For FlexBox compatibility
    @property
    def children(self):
        return self._props.get('children', [])

class FlexBox(Model):
    pass

class Viewable:
    def __init__(self, root_model):
        self._root_model = root_model
    def get_root(self, doc):
        # Always returns the underlying model for testing
        return self._root_model

# unit tests

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

def test_basic_model_single_property():
    # Test with a simple Model with one property
    m = Model(foo=42)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.39μs -> 5.09μs (64.9% faster)

def test_basic_model_multiple_properties():
    # Test with multiple properties
    m = Model(a=1, b="bar", c=3.14)
    # Sorted keys: a, b, c
    codeflash_output = bokeh_repr(m) # 12.1μs -> 8.86μs (36.3% faster)

def test_basic_model_ignored_properties():
    # Test that ignored properties are omitted
    m = Model(foo=1, children=[Model(bar=2)], text="hello", name="n", toolbar="tb")
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.79μs -> 5.97μs (47.3% faster)

def test_basic_model_custom_ignored():
    # Test custom ignored list
    m = Model(foo=1, bar=2, baz=3)
    codeflash_output = bokeh_repr(m, ignored=['foo', 'baz']); result = codeflash_output # 8.50μs -> 5.60μs (51.7% faster)

def test_basic_model_property_model_instance():
    # Test property which is itself a Model
    child = Model(child_prop=5)
    m = Model(foo=child)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 9.29μs -> 6.66μs (39.5% faster)

def test_basic_model_property_long_value():
    # Test property value longer than 30 chars gets truncated
    long_str = "x" * 40
    m = Model(foo=long_str)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.01μs -> 5.05μs (58.5% faster)

def test_basic_flexbox_children_repr():
    # Test FlexBox with children
    child1 = Model(foo=1)
    child2 = Model(bar=2)
    fb = FlexBox(children=[child1, child2], baz=3)
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 8.02μs -> 5.20μs (54.3% faster)
    # Should show children in indented list
    expected = (
        "FlexBox(children=[\n"
        "  Model(foo=1),\n"
        "  Model(bar=2),\n"
        "], baz=3)"
    )


def test_edge_empty_model():
    # Model with no properties
    m = Model()
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.27μs -> 3.89μs (112% faster)

def test_edge_model_all_ignored():
    # All properties are ignored
    m = Model(children=[Model()], text="t", name="n", toolbar="tb")
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.26μs -> 5.15μs (60.5% faster)

def test_edge_flexbox_no_children():
    # FlexBox with no children
    fb = FlexBox(baz=7)
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 7.74μs -> 4.74μs (63.4% faster)
    expected = (
        "FlexBox(children=[\n"
        "], baz=7)"
    )

def test_edge_flexbox_nested_children():
    # FlexBox with nested FlexBox
    child1 = Model(foo=1)
    child2 = FlexBox(children=[Model(bar=2)], baz=3)
    fb = FlexBox(children=[child1, child2], baz=4)
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 8.06μs -> 5.20μs (55.1% faster)
    expected = (
        "FlexBox(children=[\n"
        "  Model(foo=1),\n"
        "  FlexBox(children=[\n"
        "    Model(bar=2),\n"
        "], baz=3),\n"
        "], baz=4)"
    )

def test_edge_property_none():
    # Property explicitly set to None should be omitted
    m = Model(foo=None, bar=1)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 7.20μs -> 4.42μs (63.0% faster)

def test_edge_property_empty_string_and_zero():
    # Test empty string and zero values
    m = Model(foo="", bar=0)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.61μs -> 5.92μs (45.5% faster)

def test_edge_property_special_chars():
    # Property with special characters
    m = Model(foo="hello\nworld", bar="tab\tend")
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.80μs -> 5.74μs (53.3% faster)

def test_edge_property_long_repr_of_model():
    # Model property with long repr
    class LongReprModel(Model):
        def __repr__(self):
            return "LongReprModel(" + "x"*50 + ")"
    child = LongReprModel()
    m = Model(foo=child)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.63μs -> 5.67μs (52.4% faster)

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

def test_large_model_many_properties():
    # Model with many properties
    props = {f'prop{i}': i for i in range(100)}
    m = Model(**props)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 39.4μs -> 40.4μs (2.38% slower)
    # All properties should be present, sorted
    for i in range(100):
        pass

def test_large_flexbox_many_children():
    # FlexBox with many children
    children = [Model(foo=i) for i in range(100)]
    fb = FlexBox(children=children, baz="big")
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 8.35μs -> 5.40μs (54.5% faster)
    # Should contain all children
    for i in range(100):
        pass

def test_large_nested_flexbox():
    # Deeply nested FlexBox structure
    depth = 10
    fb = Model(foo="leaf")
    for i in range(depth):
        fb = FlexBox(children=[fb], level=i)
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 7.86μs -> 5.15μs (52.7% faster)
    # Should have FlexBox nested depth times
    count = result.count("FlexBox(children=[")

def test_large_property_long_string():
    # Property with very long string
    long_str = "y" * 1000
    m = Model(foo=long_str)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 10.5μs -> 7.21μs (45.0% faster)

def test_large_flexbox_children_truncation():
    # FlexBox with child containing long string property
    child = Model(foo="z"*1000)
    fb = FlexBox(children=[child], baz="ok")
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 7.96μs -> 5.19μs (53.2% faster)

def test_large_model_property_is_model_many_times():
    # Model with many Model properties
    props = {f'model{i}': Model(foo=i) for i in range(50)}
    m = Model(**props)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 38.1μs -> 37.0μs (2.95% 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.
#------------------------------------------------
import textwrap

# imports
import pytest
from panel.io.model import bokeh_repr


# Minimal mocks for bokeh models and panel viewable
class Model:
    """Minimal mock of bokeh.models.Model for testing."""
    def __init__(self, **props):
        self._props = props.copy()
        self.children = props.get("children", [])
    def properties_with_values(self, include_defaults=False):
        # Return all properties except children if not present
        return {k: v for k, v in self._props.items() if k != "children"}
    def __repr__(self):
        return f"{type(self).__name__}({self._props})"

class FlexBox(Model):
    """Minimal mock of bokeh.models.FlexBox for testing."""
    def __init__(self, children=None, **props):
        super().__init__(**props)
        self.children = children if children is not None else []

class Viewable:
    """Minimal mock of panel.viewable.Viewable for testing."""
    def __init__(self, root_model):
        self._root_model = root_model
    def get_root(self, doc):
        return self._root_model

# unit tests

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

def test_basic_model_single_property():
    # Test model with one property
    m = Model(foo=1)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.04μs -> 5.14μs (56.5% faster)

def test_basic_model_multiple_properties():
    # Test model with several properties
    m = Model(foo=1, bar="baz", qux=None)
    # Properties are sorted by name
    expected = "Model(bar='baz', foo=1, qux=None)"
    codeflash_output = bokeh_repr(m) # 9.13μs -> 6.42μs (42.2% faster)

def test_basic_model_with_model_property():
    # Test model with a property that is itself a Model
    child = Model(child_prop=42)
    parent = Model(child=child, foo='bar')
    # 'child' is in the default ignored set, so it should not appear
    codeflash_output = bokeh_repr(parent) # 12.1μs -> 9.28μs (30.3% faster)

def test_basic_flexbox_children_repr():
    # Test FlexBox with children
    child1 = Model(foo=1)
    child2 = Model(bar=2)
    fb = FlexBox(children=[child1, child2], baz='qux')
    # Children should be indented and shown as Model(foo=1), Model(bar=2)
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 7.70μs -> 4.87μs (58.2% faster)


def test_empty_model():
    # Model with no properties
    m = Model()
    codeflash_output = bokeh_repr(m) # 8.44μs -> 4.36μs (93.7% faster)

def test_model_with_long_property_value():
    # Property value longer than 30 characters should be truncated
    long_str = "x" * 50
    m = Model(long=long_str)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 8.21μs -> 5.48μs (49.9% faster)
    # Should truncate to first 30 characters + '...'
    expected = f"Model(long='{long_str[:30]}...')"

def test_model_with_ignored_properties():
    # Properties in the ignored set should be omitted
    m = Model(children=[Model(foo=1)], text="hi", name="test", foo=2)
    # Only 'foo' should be shown
    codeflash_output = bokeh_repr(m) # 8.79μs -> 5.87μs (49.8% faster)

def test_model_with_custom_ignored_set():
    # Custom ignored set should override default
    m = Model(foo=1, bar=2, baz=3)
    ignored = {"foo", "baz"}
    codeflash_output = bokeh_repr(m, ignored=ignored); result = codeflash_output # 8.80μs -> 5.98μs (47.2% faster)

def test_flexbox_with_no_children():
    # FlexBox with no children
    fb = FlexBox(children=[], foo='bar')
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 7.92μs -> 4.87μs (62.8% faster)
    # Should not show any Model(...) inside children

def test_flexbox_nested():
    # FlexBox with nested FlexBox children
    grandchild = Model(grand='child')
    child = FlexBox(children=[grandchild], foo='child')
    parent = FlexBox(children=[child], bar='parent')
    codeflash_output = bokeh_repr(parent); result = codeflash_output # 7.60μs -> 5.03μs (51.0% faster)


def test_model_with_property_value_model_and_non_model():
    # Should represent Model property as ClassName(), others as repr
    child = Model(child_prop=1)
    parent = Model(foo=child, bar=123)
    # 'foo' is not ignored, so should show as Model()
    codeflash_output = bokeh_repr(parent, ignored={"bar"}); result = codeflash_output # 12.8μs -> 8.97μs (42.3% faster)

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

def test_large_number_of_properties():
    # Model with many properties
    props = {f"p{i}": i for i in range(100)}
    m = Model(**props)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 40.0μs -> 38.6μs (3.59% faster)
    # All properties should be shown, sorted by name
    for i in range(100):
        pass
    # Should not truncate any property name

def test_large_flexbox_many_children():
    # FlexBox with many children
    children = [Model(foo=i) for i in range(100)]
    fb = FlexBox(children=children, bar='baz')
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 8.12μs -> 4.99μs (62.8% faster)
    # Should show all children
    for i in range(100):
        pass

def test_large_nested_flexbox():
    # Deeply nested FlexBox tree
    leaf = Model(val='leaf')
    mid = FlexBox(children=[leaf], mid='node')
    root = FlexBox(children=[mid], root='node')
    codeflash_output = bokeh_repr(root); result = codeflash_output # 7.53μs -> 4.80μs (56.9% faster)

def test_large_property_value_truncation():
    # All long property values should be truncated
    long_val = "y" * 100
    props = {f"p{i}": long_val for i in range(10)}
    m = Model(**props)
    codeflash_output = bokeh_repr(m); result = codeflash_output # 13.7μs -> 11.2μs (23.1% faster)
    for i in range(10):
        pass

def test_performance_large_scale(monkeypatch):
    # Test that function completes for large input within reasonable time
    import time
    children = [Model(foo=i, bar="z"*50) for i in range(500)]
    fb = FlexBox(children=children, baz="x"*50)
    start = time.time()
    codeflash_output = bokeh_repr(fb); result = codeflash_output # 9.15μs -> 6.28μs (45.8% faster)
    duration = time.time() - start
    # Should show truncated values and all children
    for i in range(500):
        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-bokeh_repr-mha36pyf and push.

Codeflash

The optimization achieves a **32% speedup** by eliminating repeated import overhead through **import caching**. 

**Key optimization:**
- **Import caching**: The original code imports `Viewable` on every function call (line profiler shows 194,845ns spent on import across 40 calls). The optimized version caches the import using function attributes - importing only once and storing `Viewable` as `bokeh_repr._viewable` for subsequent calls.

**Performance impact:**
The line profiler clearly shows the difference:
- **Original**: 194,845ns (14.7% of total time) spent on `from ..viewable import Viewable` across 40 calls
- **Optimized**: Only 6,959ns + 565ns (0.7% of total time) for the one-time import and caching

**Why this works:**
Python imports involve module lookup, loading, and namespace operations. By caching the imported class as a function attribute, we avoid this overhead on subsequent calls while maintaining the same functionality.

**Test case benefits:**
The optimization is particularly effective for:
- **Small-to-medium models** (8-15μs → 4-6μs, ~50-60% faster): The import overhead was a significant portion of total execution time
- **Large-scale operations** show smaller relative gains (2-4% faster) since property processing dominates execution time, making import overhead less significant

The caching approach preserves all original behavior while dramatically reducing the most expensive operation in the function's hot path.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 October 28, 2025 04:48
@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