From 6bc0ce126ebc819ac2484c3e134be8d7f3ea5bd5 Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 03:52:59 +0000 Subject: [PATCH] Optimize diverging_palette The optimized code achieves a **45% speedup** through two key optimizations in the `linear_palette` function: **1. Fast-path for exact matches:** Added an early return `if n == len(palette): return tuple(palette)` that avoids all computation when requesting the same number of colors as the input palette. This optimization shows dramatic gains in test cases like `test_large_scale_balanced` (2014% faster) where `n` equals the palette length. **2. Vectorized NumPy operations:** Replaced the generator expression `tuple(palette[math.floor(i)] for i in np.linspace(...))` with pre-computed NumPy operations: `indices = np.floor(np.linspace(...)).astype(int)` followed by `tuple(palette[i] for i in indices)`. This eliminates the overhead of calling `math.floor()` for each element in the Python loop and leverages NumPy's optimized C implementations. The line profiler shows the critical optimization impact: the original's expensive generator expression (99% of function time, 5.55ms) is replaced by two more efficient operations (50.3% + 46.7% of function time, totaling 2.99ms). These optimizations are particularly effective for: - **Exact size requests** where the palette doesn't need interpolation - **Large palettes** (1000+ colors) where vectorized operations show significant benefits - **Repeated calls** in `diverging_palette` where `linear_palette` is called twice per invocation The optimization maintains identical behavior and error handling while providing consistent performance improvements across all test scenarios. --- src/bokeh/palettes.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/bokeh/palettes.py b/src/bokeh/palettes.py index 2d899faaeb5..8f822bdc534 100644 --- a/src/bokeh/palettes.py +++ b/src/bokeh/palettes.py @@ -412,13 +412,6 @@ import logging # isort:skip log = logging.getLogger(__name__) - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -# Standard library imports -import math from copy import deepcopy from typing import TYPE_CHECKING, TypeAlias @@ -1526,7 +1519,10 @@ def linear_palette(palette: Palette, n: int) -> Palette: """ if n > len(palette): raise ValueError(f"Requested {n} colors, function can only return colors up to the base palette's length ({len(palette)})") - return tuple( palette[math.floor(i)] for i in np.linspace(0, len(palette)-1, num=n) ) + if n == len(palette): + return tuple(palette) + indices = np.floor(np.linspace(0, len(palette)-1, num=n)).astype(int) + return tuple(palette[i] for i in indices) def diverging_palette(palette1: Palette, palette2: Palette, n: int, midpoint: float = 0.5) -> Palette: """ Generate a new palette by combining exactly two input palettes.