Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ stable (10 byte-identical frames), so progressive renderers are charged until
their last chunk lands.

<p align="center">
<img src="spec/assets/ux-render-time.png" alt="Time until every point is on screen, 10k to 100M points, for XY, Matplotlib, and Plotly. Lower is better." width="1200">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="spec/assets/ux-render-time-dark.png">
<img src="spec/assets/ux-render-time.png" alt="Time until every point is on screen, 10k to 100M points, for XY, Matplotlib, and Plotly. Lower is better." width="1200">
</picture>
</p>

XY holds **0.071 s at 10k and 0.081 s at 100M**, flat across four orders of
Expand Down
7 changes: 6 additions & 1 deletion benchmarks/plot_ux.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from typing import Any

import xy
from xy._benchmark_theme import BENCHMARK_DARK_THEME, BENCHMARK_LIGHT_THEME

GIB = 2**30

Expand Down Expand Up @@ -68,6 +69,7 @@ def build(
sizes: list[int],
arms: list[str],
metric: str,
color_scheme: str = "light",
) -> xy.Chart:
marks: list[Any] = []
notes: list[Any] = []
Expand Down Expand Up @@ -149,9 +151,11 @@ def build(
domain = (0.0, 5.8)

decades = [1e4, 1e5, 1e6, 1e7, 1e8]
theme = BENCHMARK_DARK_THEME if color_scheme == "dark" else BENCHMARK_LIGHT_THEME
return xy.line_chart(
*marks,
*notes,
xy.theme(**theme),
xy.legend(show=True, loc="upper left"),
xy.modebar(show=False),
xy.tooltip(format={"y": ".3f"}),
Expand Down Expand Up @@ -183,6 +187,7 @@ def main() -> None:
parser.add_argument("--arms", default="", help="comma-separated subset")
parser.add_argument("--suffix", default="")
parser.add_argument("--scale", type=int, default=2)
parser.add_argument("--color-scheme", choices=("light", "dark"), default="light")
args = parser.parse_args()

rows: dict[tuple[str, int], dict[str, Any]] = {}
Expand All @@ -198,7 +203,7 @@ def main() -> None:

args.out_dir.mkdir(parents=True, exist_ok=True)
for metric, stem in (("time", "render-time"), ("memory", "python-memory")):
chart = build(rows, sizes, arms, metric)
chart = build(rows, sizes, arms, metric, color_scheme=args.color_scheme)
path = args.out_dir / f"ux-{stem}{args.suffix}.png"
chart.to_png(str(path), scale=args.scale)
print(f"wrote {path}")
Expand Down
20 changes: 20 additions & 0 deletions docs/app/tests/test_docs_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,26 @@ def test_docs_app_configures_the_reflex_xy_adapter() -> None:
assert any(isinstance(plugin, reflex_xy.XYPlugin) for plugin in config.plugins)


def test_benchmark_demo_uses_the_shared_theme() -> None:
from xy_docs.demos import benchmark_charts

from xy._benchmark_theme import (
benchmark_chart_class,
benchmark_live_theme,
)

assert benchmark_chart_class() == benchmark_charts._CHART_CLASS

live_theme = benchmark_live_theme()
assert benchmark_charts._theme().style == {
"background": live_theme["background"],
"--chart-bg": live_theme["plot_background"],
"--chart-grid": live_theme["grid_color"],
"--chart-axis": live_theme["axis_color"],
"--chart-text": live_theme["text_color"],
}


def test_docs_app_does_not_override_the_builtin_toolbar_palette() -> None:
assert not any(key.startswith("--chart-modebar-") for key in _CHART_STYLE)

Expand Down
20 changes: 6 additions & 14 deletions docs/app/xy_docs/demos/benchmark_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import reflex_xy

import xy
from xy._benchmark_theme import (
benchmark_chart_class,
benchmark_live_theme,
)

XY_COLOR = "#6E56CF"
XY_EXACT_COLOR = "#A594E8"
Expand All @@ -19,13 +23,7 @@
("Plotly", PLOTLY_COLOR),
)

_CHART_CLASS = (
"w-full [--benchmark-bg:#ffffff] [--benchmark-plot:#fcfcfd] "
"[--benchmark-grid:#e8e8ec] [--benchmark-axis:#d9d9e0] "
"[--benchmark-text:#60646c] dark:[--benchmark-bg:#09090b] "
"dark:[--benchmark-plot:#111113] dark:[--benchmark-grid:#27272a] "
"dark:[--benchmark-axis:#3f3f46] dark:[--benchmark-text:#d4d4d8]"
)
_CHART_CLASS = benchmark_chart_class()
_CARD_CLASS = (
"w-full overflow-hidden rounded-xl border border-secondary-4 bg-white "
"shadow-[0_12px_32px_#1c20240f] dark:bg-black"
Expand All @@ -34,13 +32,7 @@

def _theme() -> xy.Theme:
"""Return the neutral benchmark theme shared by the docs site."""
return xy.theme(
background="var(--benchmark-bg, #ffffff)",
plot_background="var(--benchmark-plot, #fcfcfd)",
grid_color="var(--benchmark-grid, #e8e8ec)",
axis_color="var(--benchmark-axis, #d9d9e0)",
text_color="var(--benchmark-text, #60646c)",
)
return xy.theme(**benchmark_live_theme())


def _legend() -> rx.Component:
Expand Down
2 changes: 2 additions & 0 deletions docs/overview/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

.venv/bin/python benchmarks/summarize_ux.py /path/to/xy-ux-suite
.venv/bin/python benchmarks/plot_ux.py /path/to/xy-ux-suite --out-dir charts
.venv/bin/python benchmarks/plot_ux.py /path/to/xy-ux-suite --out-dir charts \
--color-scheme dark --suffix=-dark
```

Keep results separated by environment. Hardware WebGL and SwiftShader rows are
Expand Down
50 changes: 50 additions & 0 deletions python/xy/_benchmark_theme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Shared visual tokens for static and interactive benchmark charts.

This is an internal module for the repository's benchmark tooling and docs app,
not part of the public ``xy`` API.
"""

from __future__ import annotations

BENCHMARK_LIGHT_THEME = {
"background": "#ffffff",
"plot_background": "#fcfcfd",
"grid_color": "#e8e8ec",
"axis_color": "#d9d9e0",
"text_color": "#60646c",
}
BENCHMARK_DARK_THEME = {
"background": "#09090b",
"plot_background": "#111113",
"grid_color": "#27272a",
"axis_color": "#3f3f46",
"text_color": "#d4d4d8",
}
BENCHMARK_CSS_VARIABLES = {
"background": "--benchmark-bg",
"plot_background": "--benchmark-plot",
"grid_color": "--benchmark-grid",
"axis_color": "--benchmark-axis",
"text_color": "--benchmark-text",
}


def benchmark_live_theme() -> dict[str, str]:
"""Return theme values backed by the docs app's light/dark CSS variables."""
return {
token: f"var({css_variable}, {BENCHMARK_LIGHT_THEME[token]})"
for token, css_variable in BENCHMARK_CSS_VARIABLES.items()
}


def benchmark_chart_class() -> str:
"""Return the docs class that assigns both color schemes to the CSS variables."""
light = (
f"[{css_variable}:{BENCHMARK_LIGHT_THEME[token]}]"
for token, css_variable in BENCHMARK_CSS_VARIABLES.items()
)
dark = (
f"dark:[{css_variable}:{BENCHMARK_DARK_THEME[token]}]"
for token, css_variable in BENCHMARK_CSS_VARIABLES.items()
)
return " ".join(("w-full", *light, *dark))
Binary file added spec/assets/ux-render-time-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified spec/assets/ux-render-time.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions tests/test_benchmark_theme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Shared benchmark theme contract."""

from __future__ import annotations

from benchmarks.plot_ux import build

from xy._benchmark_theme import (
BENCHMARK_CSS_VARIABLES,
BENCHMARK_DARK_THEME,
BENCHMARK_LIGHT_THEME,
benchmark_chart_class,
benchmark_live_theme,
)

_THEME_STYLE_KEYS = {
"background": "background",
"plot_background": "--chart-bg",
"grid_color": "--chart-grid",
"axis_color": "--chart-axis",
"text_color": "--chart-text",
}


def test_static_benchmark_export_uses_shared_theme() -> None:
rows = {("xy", 10_000): {"status": "ok", "visible_complete_ms": 71}}
for color_scheme, expected_theme in (
("light", BENCHMARK_LIGHT_THEME),
("dark", BENCHMARK_DARK_THEME),
):
spec, _ = (
build(rows, [10_000], ["xy"], "time", color_scheme=color_scheme)
.figure()
.build_payload()
)
style = spec["dom"]["style"]
assert {
token: style[style_key] for token, style_key in _THEME_STYLE_KEYS.items()
} == expected_theme


def test_live_benchmark_theme_derives_both_color_schemes() -> None:
assert benchmark_live_theme() == {
token: f"var({css_variable}, {BENCHMARK_LIGHT_THEME[token]})"
for token, css_variable in BENCHMARK_CSS_VARIABLES.items()
}

classes = set(benchmark_chart_class().split())
assert "w-full" in classes
for token, css_variable in BENCHMARK_CSS_VARIABLES.items():
assert f"[{css_variable}:{BENCHMARK_LIGHT_THEME[token]}]" in classes
assert f"dark:[{css_variable}:{BENCHMARK_DARK_THEME[token]}]" in classes
Loading