Skip to content

perf(points): strip alpha on unique colours, not once per point#692

Merged
timtreis merged 2 commits into
mainfrom
perf/points-dedup-hex-no-alpha
Jun 6, 2026
Merged

perf(points): strip alpha on unique colours, not once per point#692
timtreis merged 2 commits into
mainfrom
perf/points-dedup-hex-no-alpha

Conversation

@timtreis
Copy link
Copy Markdown
Member

@timtreis timtreis commented Jun 6, 2026

What & why

Second item from the profiling write-up in #690 (after the shapes patch-build fix in #691).

In the categorical datashader path of _render_points, the alpha component was stripped
from every point's hex colour:

color_vector = np.asarray([_hex_no_alpha(c) for c in color_vector])

_hex_no_alpha is a Python per-string parser/validator, so this ran once per point — but
a categorical colour vector only holds a handful of distinct strings (one per category).

Change

Deduplicate with np.unique(..., return_inverse=True): strip alpha on the unique values
and map back.

unique_hex, inverse = np.unique(color_vector, return_inverse=True)
color_vector = np.asarray([_hex_no_alpha(c) for c in unique_hex])[inverse.reshape(-1)]

(reshape(-1) guards against the numpy 2.0 return_inverse shape change.)

Correctness — byte-identical output

Rendered 6 categorical point scenarios on this branch and on main, comparing the Agg
RGBA buffers exactly (np.array_equal): all identical.

genes default / palette / alpha · cat7 (7 categories) default / groups /
groups+na_color

Performance

The hex strip drops from ~67 ms to ~6 ms at 50k points (the operation is bit-identical).
End-to-end render_points(color=…):

n_points main this PR
50k ~332 ms ~301 ms (~9%)
200k ~598 ms ~503 ms (~16%)

The saving scales with point count.

Notes

  • No public API or behaviour change; output is identical.
  • Local pre-commit (ruff check/format, mypy) passes. The test_plot_* visual baselines
    run on CI; output is byte-identical to main, so they should pass unchanged.

Addresses the points item in #690.

timtreis added 2 commits June 6, 2026 20:00
In the categorical datashader points path, _render_points stripped the alpha
component from every point's hex colour:

    color_vector = np.asarray([_hex_no_alpha(c) for c in color_vector])

_hex_no_alpha is a Python per-string parser, so this ran once per point even
though a categorical colour vector holds only a handful of distinct strings.
Deduplicate via np.unique(return_inverse=True): strip alpha on the unique
values and map back.

Output is unchanged (RGBA buffers byte-identical to main across 6 categorical
point scenarios: genes/cat7, palette, groups, na_color, alpha). The hex strip
drops from ~67 ms to ~6 ms at 50k points; end-to-end render_points is ~9%
faster at 50k and ~16% at 200k, scaling with point count.
np.unique(return_inverse=True) already yields a 1-D inverse for the 1-D
color_vector, so reshape(-1) was a no-op. Output unchanged (6-scenario
parity holds).
@timtreis timtreis merged commit 9b7c013 into main Jun 6, 2026
5 of 8 checks passed
@timtreis timtreis deleted the perf/points-dedup-hex-no-alpha branch June 6, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant