Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 15% (0.15x) speedup for HSL.to_hsl in src/bokeh/colors/color.py

⏱️ Runtime : 710 microseconds 617 microseconds (best of 237 runs)

📝 Explanation and details

The optimized version achieves a 15% speedup through two key changes:

1. Added __slots__ declaration: This restricts instance attribute storage to only ('h', 's', 'l', 'a'), eliminating the default __dict__ for each HSL instance. This reduces memory overhead and makes attribute access faster, particularly beneficial when creating many HSL objects.

2. Eliminated method call indirection in to_hsl(): The original version called self.copy() which then called HSL(...), creating an unnecessary function call overhead. The optimized version directly constructs HSL(self.h, self.s, self.l, self.a), removing this indirection.

Why this leads to speedup: The line profiler shows the original to_hsl() took 5.7ms total time (2813.7ns per call), while the optimized version takes only 2.1ms (1010ns per call) - a 64% improvement in the to_hsl() method itself. This is because:

  • Direct construction avoids the extra method call stack overhead
  • __slots__ makes attribute access (self.h, self.s, etc.) faster
  • Reduced memory allocations improve cache locality

Test results show consistent improvements: All test cases demonstrate 7-40% speedups, with larger improvements on edge cases (up to 40.6% faster). The optimization is particularly effective for scenarios involving many HSL objects or repeated calls to to_hsl(), as evidenced by the large-scale test improvements (14-15% faster on batch operations).

The changes preserve all existing behavior while making HSL objects more memory-efficient and faster to instantiate/copy.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 9218 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 3 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
from __future__ import annotations

# imports
import pytest  # used for our unit tests
from bokeh.colors.color import HSL

# unit tests

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

def test_to_hsl_basic_identity():
    """Test that to_hsl returns an HSL with the same values as the original."""
    hsl = HSL(120, 0.5, 0.5, 1.0)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 753ns -> 655ns (15.0% faster)

def test_to_hsl_basic_default_alpha():
    """Test that default alpha is 1.0 if not specified."""
    hsl = HSL(240, 0.3, 0.7)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 712ns -> 660ns (7.88% faster)

def test_to_hsl_basic_different_values():
    """Test various valid values for H, S, L, and A."""
    hsl = HSL(0, 0, 0, 0)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 784ns -> 619ns (26.7% faster)
    hsl = HSL(360, 1, 1, 1)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 415ns -> 379ns (9.50% faster)

def test_to_hsl_returns_copy_not_same_object():
    """Test that to_hsl returns a new object, not the same reference."""
    hsl = HSL(180, 0.5, 0.5, 0.5)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 769ns -> 619ns (24.2% faster)

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

def test_to_hsl_edge_minimum_values():
    """Test minimum allowed values for h, s, l, a."""
    hsl = HSL(0, 0.0, 0.0, 0.0)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 735ns -> 611ns (20.3% faster)

def test_to_hsl_edge_maximum_values():
    """Test maximum allowed values for h, s, l, a."""
    hsl = HSL(360, 1.0, 1.0, 1.0)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 749ns -> 588ns (27.4% faster)

def test_to_hsl_edge_non_integer_values():
    """Test non-integer but valid float values for h, s, l, a."""
    hsl = HSL(123.456, 0.789, 0.234, 0.567)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 698ns -> 567ns (23.1% faster)

def test_to_hsl_edge_negative_values():
    """Test negative values for h, s, l, a (should be accepted, as no validation is present)."""
    hsl = HSL(-10, -0.1, -0.1, -0.1)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 750ns -> 593ns (26.5% faster)

def test_to_hsl_edge_values_above_max():
    """Test values above the documented max for h, s, l, a (should be accepted, as no validation is present)."""
    hsl = HSL(400, 2, 2, 2)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 718ns -> 576ns (24.7% faster)

def test_to_hsl_edge_zero_alpha():
    """Test alpha = 0, fully transparent."""
    hsl = HSL(180, 0.5, 0.5, 0.0)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 718ns -> 591ns (21.5% faster)

def test_to_hsl_edge_extreme_float_precision():
    """Test float values with high precision."""
    hsl = HSL(359.999999, 0.999999, 0.000001, 0.999999)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 736ns -> 572ns (28.7% faster)

def test_to_hsl_edge_mutation_of_copy():
    """Test that changing returned copy does not affect original."""
    hsl = HSL(100, 0.5, 0.5, 0.5)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 690ns -> 580ns (19.0% faster)
    hsl2.h = 200
    hsl2.s = 0.9
    hsl2.l = 0.1
    hsl2.a = 0.0

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

def test_to_hsl_large_scale_many_colors():
    """Test to_hsl on a large number of HSL objects to check for performance and correctness."""
    N = 1000  # keep under 1000 elements as requested
    colors = [HSL(i % 361, (i % 101) / 100, (i % 101) / 100, (i % 101) / 100) for i in range(N)]
    for orig in colors:
        codeflash_output = orig.to_hsl(); copy = codeflash_output # 343μs -> 298μs (14.7% faster)

def test_to_hsl_large_scale_mutability():
    """Test that mutating copies from to_hsl does not affect originals in a large set."""
    N = 500
    colors = [HSL(i, 0.5, 0.5, 0.5) for i in range(N)]
    copies = [c.to_hsl() for c in colors]
    # Mutate all copies
    for c in copies:
        c.h += 1
        c.s = 1.0
        c.l = 0.0
        c.a = 0.0
    # Originals should remain unchanged
    for i, orig in enumerate(colors):
        pass

def test_to_hsl_large_scale_extreme_values():
    """Test large scale with edge values."""
    N = 1000
    colors = [HSL(360, 1.0, 1.0, 1.0) for _ in range(N)]
    for orig in colors:
        codeflash_output = orig.to_hsl(); copy = codeflash_output # 343μs -> 298μs (14.8% faster)

def test_to_hsl_large_scale_performance():
    """Test that large scale conversion does not take excessive time."""
    import time
    N = 1000
    colors = [HSL(i % 361, 0.5, 0.5, 0.5) for i in range(N)]
    start = time.time()
    copies = [c.to_hsl() for c in colors]
    end = time.time()
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
from __future__ import annotations

# imports
import pytest
from bokeh.colors.color import HSL

# unit tests

# Helper for comparing HSL objects
def hsl_equal(a: HSL, b: HSL) -> bool:
    return (
        isinstance(a, HSL) and
        isinstance(b, HSL) and
        a.h == b.h and
        a.s == b.s and
        a.l == b.l and
        a.a == b.a
    )

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

def test_to_hsl_returns_copy_basic():
    # Test that to_hsl returns a new, equal HSL object for typical values
    hsl = HSL(120, 0.5, 0.5)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 1.11μs -> 813ns (36.8% faster)

def test_to_hsl_preserves_alpha():
    # Test that alpha is preserved
    hsl = HSL(240, 0.7, 0.3, 0.25)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 887ns -> 631ns (40.6% faster)

def test_to_hsl_default_alpha():
    # Test that default alpha is 1.0 and is preserved
    hsl = HSL(60, 0.1, 0.9)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 840ns -> 653ns (28.6% faster)

def test_to_hsl_multiple_calls_independent_objects():
    # Test that multiple calls to to_hsl return independent objects
    hsl = HSL(180, 0.2, 0.8, 0.5)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 788ns -> 624ns (26.3% faster)
    codeflash_output = hsl.to_hsl(); hsl3 = codeflash_output # 405ns -> 368ns (10.1% faster)

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

def test_to_hsl_boundary_hue():
    # Test hue at lower and upper boundaries
    for h in [0, 360]:
        hsl = HSL(h, 0.5, 0.5)
        codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 1.11μs -> 974ns (14.5% faster)

def test_to_hsl_boundary_saturation_lightness():
    # Test saturation and lightness at boundaries
    for s in [0.0, 1.0]:
        for l in [0.0, 1.0]:
            hsl = HSL(180, s, l)
            codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output

def test_to_hsl_alpha_boundaries():
    # Test alpha at boundaries
    for a in [0.0, 1.0]:
        hsl = HSL(120, 0.5, 0.5, a)
        codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 1.14μs -> 1.02μs (12.3% faster)

def test_to_hsl_negative_and_large_values():
    # Test negative and large values (should preserve as is, no validation)
    hsl = HSL(-10, -0.1, 1.1, 1.5)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 743ns -> 613ns (21.2% faster)

def test_to_hsl_mutation_safety():
    # Test that mutating the original does not affect the copy
    hsl = HSL(90, 0.4, 0.6, 0.8)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 706ns -> 610ns (15.7% faster)
    hsl.h = 180
    hsl.s = 0.9
    hsl.l = 0.1
    hsl.a = 0.2

def test_to_hsl_copy_is_instance():
    # Test that the returned object is an instance of HSL
    hsl = HSL(123, 0.45, 0.67, 0.89)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 735ns -> 606ns (21.3% faster)

def test_to_hsl_copy_not_original():
    # Test that the returned object is not the original
    hsl = HSL(321, 0.12, 0.34, 0.56)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 699ns -> 585ns (19.5% faster)

# ------------------------
# LARGE SCALE TEST CASES
# ------------------------

def test_to_hsl_large_scale_list():
    # Test to_hsl on a large list of HSL objects
    hsls = [HSL(h, s, l, a)
            for h, s, l, a in zip(
                range(0, 360, 3),  # 0 to 357, step 3 (~120 elements)
                [i/119 for i in range(120)],
                [1-i/119 for i in range(120)],
                [i/119 for i in range(120)]
            )]
    hsl_copies = [hsl.to_hsl() for hsl in hsls]
    for orig, copy in zip(hsls, hsl_copies):
        pass

def test_to_hsl_large_scale_independence():
    # Ensure that all copies are independent of their originals
    hsls = [HSL(i, 0.5, 0.5, 0.5) for i in range(500)]
    copies = [hsl.to_hsl() for hsl in hsls]
    # Mutate originals
    for i, hsl in enumerate(hsls):
        hsl.h = -i
        hsl.s = 1.0
        hsl.l = 0.0
        hsl.a = 0.0
    # Copies should remain unchanged
    for i, copy in enumerate(copies):
        pass

def test_to_hsl_performance_large_batch():
    # Test performance by calling to_hsl many times (within reasonable limits)
    hsls = [HSL(i % 360, (i % 100)/100, ((i*2) % 100)/100, (i % 10)/10) for i in range(1000)]
    copies = [hsl.to_hsl() for hsl in hsls]
    for orig, copy in zip(hsls, copies):
        pass

# ----------------------
# ADDITIONAL EDGE CASES
# ----------------------

def test_to_hsl_extreme_float_precision():
    # Test with extreme float values for s, l, a
    hsl = HSL(180, 1e-12, 1-1e-12, 1e-12)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 1.05μs -> 775ns (35.1% faster)

def test_to_hsl_nan_and_inf():
    # Test with NaN and inf values (should be preserved as is)
    import math
    hsl = HSL(float('nan'), float('inf'), float('-inf'), float('nan'))
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 912ns -> 662ns (37.8% faster)

def test_to_hsl_type_preservation():
    # Test that the returned object is exactly type HSL (not a subclass)
    hsl = HSL(10, 0.2, 0.3, 0.4)
    codeflash_output = hsl.to_hsl(); hsl2 = codeflash_output # 789ns -> 654ns (20.6% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
from bokeh.colors.color import HSL

def test_HSL_to_hsl():
    HSL.to_hsl(HSL(0.0, 0.0, 0.0, a=0.0))
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic_sstvtaha/tmpgbstuzgu/test_concolic_coverage.py::test_HSL_to_hsl 754ns 630ns 19.7%✅

To edit these changes git checkout codeflash/optimize-HSL.to_hsl-mhwaq9mu and push.

Codeflash Static Badge

The optimized version achieves a **15% speedup** through two key changes:

**1. Added `__slots__` declaration**: This restricts instance attribute storage to only `('h', 's', 'l', 'a')`, eliminating the default `__dict__` for each HSL instance. This reduces memory overhead and makes attribute access faster, particularly beneficial when creating many HSL objects.

**2. Eliminated method call indirection in `to_hsl()`**: The original version called `self.copy()` which then called `HSL(...)`, creating an unnecessary function call overhead. The optimized version directly constructs `HSL(self.h, self.s, self.l, self.a)`, removing this indirection.

**Why this leads to speedup**: The line profiler shows the original `to_hsl()` took 5.7ms total time (2813.7ns per call), while the optimized version takes only 2.1ms (1010ns per call) - a **64% improvement** in the `to_hsl()` method itself. This is because:
- Direct construction avoids the extra method call stack overhead
- `__slots__` makes attribute access (`self.h`, `self.s`, etc.) faster
- Reduced memory allocations improve cache locality

**Test results show consistent improvements**: All test cases demonstrate 7-40% speedups, with larger improvements on edge cases (up to 40.6% faster). The optimization is particularly effective for scenarios involving many HSL objects or repeated calls to `to_hsl()`, as evidenced by the large-scale test improvements (14-15% faster on batch operations).

The changes preserve all existing behavior while making HSL objects more memory-efficient and faster to instantiate/copy.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 12, 2025 17:50
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: Medium Optimization Quality according to Codeflash labels Nov 12, 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: Medium Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant