Skip to content

feat: add reusable LedFx application performance tools - #8

Merged
shauneccles merged 3 commits into
mainfrom
feat/performance-suite
Oct 5, 2026
Merged

shauneccles merged 3 commits into
mainfrom
feat/performance-suite

Conversation

@shauneccles

Copy link
Copy Markdown
Member

The LedFx performance spikes accumulated application benchmarks and experiment results in the application tree. This adds a reusable external ledfx_performance package for measuring explicit LedFx checkouts and environments, so the production application can consume sender wheels without carrying profiling scripts or native build tooling.

The package provides whole-app effect runs, synthetic audio and media fixtures, independent DDP and websocket output checks, Python 3.15 sampling reports, and randomized adjacent before/after comparisons. Runs retain provenance, failed trials and process logs outside application source; deadlines and owned process cleanup bound each experiment. README examples document pacing, receiver limits and interpretation of results. Derived performance code retains LedFx's GPL license alongside the existing tools' MIT license.

Validation: 27 tests pass; Ruff lint/format, strict Pyrefly and actionlint pass. A real uncapped before/after full-preview pair passed, along with synthetic-DSP Melt & Sparkle and generated-GIF smoke runs. The dedicated quality workflow also supports manual runs.

@shauneccles
shauneccles merged commit 7eede4a into main Oct 5, 2026
1 check passed
shauneccles added a commit to LedFx/LedFx that referenced this pull request Oct 5, 2026
LedFx delegates packet encoding and sending to `ledfx-senders` while
retaining application-owned serial, TCP and DTLS sessions where
required. It skips visualization preparation when no browser subscribes,
preserves configured preview cadence, and reduces repeated
allocation/conversion work in the effect pipeline.

- Uses canonical public senders for E1.31, DDP, Art-Net, OPC, OSC and
Nanoleaf, plus native vendor encoding and RGB gathering. Application RGB
frames remain NumPy arrays.
- Preserves transactional sender replacement, DNS generation checks,
rollback and ownership locks, including current-main's Art-Net
render/configuration deadlock regression. OSC configuration validation
uses the public constructor and closes its temporary probe.
- Keeps independent wire, lifecycle, destination-update, complete-write
and numerical regression tests. Reusable application benchmarks live in
[LedFx/ledfx-tools](https://github.com/LedFx/ledfx-tools); protocol
microbenchmarks and native releases remain in
[ledfx-senders](https://github.com/LedFx/ledfx-senders).

The dependency requires `ledfx-senders>=0.3.0,<0.4.0`, locks [published
0.3.1](https://github.com/LedFx/ledfx-senders/releases/tag/v0.3.1), and
uses wheels; uv is configured not to build it from source. The lock
refresh changes no unrelated dependency. This delivery is based directly
on current main (`a5ec8278`), preserves its shared-release CI, and adds
no application Rust build, CI workflow, temporary sender facade,
benchmark archive or development report.

The existing measurement campaign compared main `a5ec8278` with cleanup
`091ac8b9`: 50,000 RGB pixels, CPython 3.12.14, NumPy 2.5.3, standard
asyncio, the same independent native loopback receiver, and three
randomized adjacent pairs per workload. Each trial used two seconds
warmup and eight seconds measured. Render pacing was bypassed while
Rainbow's effect update cadence remained about 60 Hz.

| Preview workload | Before render FPS | After render FPS | Median
paired ratio | Pair range |
| --- | ---: | ---: | ---: | ---: |
| No subscriber; preview pacing bypassed | 496 | 1,148 | 2.376× |
2.215–2.425× |
| Full preview; preview pacing bypassed | 681 | 719 | 1.023× |
1.009–1.103× |
| Full preview; normal 60 Hz preview limit | 1,040 | 1,101 | 1.060× |
0.980–1.127× |

These are whole-application results. The large no-subscriber gain is
substantially explained by skipping unused preview preparation and does
not establish a sender-only speedup. The small full-preview differences
overlap plausible host variation. Individual-row FPS medians differ from
the medians of paired ratios. Render/DDP throughput includes repeated
images; both preview modes deliver about 59 changed RGB images per
second. Local loopback stress results do not establish physical LED
latency or LAN/Wi-Fi capacity, and zero malformed DDP packets does not
mean zero incomplete frames.

Final validation used the real PyPI 0.3.1 CPython 3.12 wheel in a frozen
locked environment. Its hash matches PyPI and the lockfile, and all 19
installed sender package payloads match the wheel, including the native
extension. Canonical public `Frame`, all seven sender exports, and E1.31
`ChannelLayout` imports were verified. The complete application suite
passed **3,337 tests and 29 subtests**, with one skip; **116 focused
CLI, sender, lifecycle and E1.31 tests** passed after the review fixes.
The E1.31 blocked-import fixture supplies the same NumPy frame type as
the application, retains exact wire assertions and explicitly runs its
child from the repository root. All configured hooks passed, including
Ruff lint/format, isolated locked strict Pyrefly, ClassVar audit,
actionlint and zizmor. Strict Pyrefly reports zero errors with seven
existing suppressions in the standard CI dependency set. The optional
Hue extra exposes five existing typing errors and remains separate from
this cleanup.

Review fixes preserve active sender sequences and pixel buffers across
unchanged DNS resolution, retain the active frame when destinations
change, and propagate the actual tray startup/shutdown status across
pystray's setup thread using a bounded join. These fixes have focused
regressions and passed the complete suite at `c6a6ba5a`.

Hosted validation for the preceding `9303fc1c` head: [CI
37281836408](https://github.com/LedFx/LedFx/actions/runs/37281836408)
passed Linux tests on Python 3.11–3.14, Windows tests, macOS smoke
tests, wheel/sdist checks, all frozen builds and both Docker
architectures. Hosted checks for the updated head must pass
independently before merge.

This PR incorporates or replaces all production changes from #2059,
#2060, #2061 and #2062. Close that obsolete stack as superseded after
this PR merges; no additional patch or rebase of those branches is
needed. Reusable performance tooling remains in [ledfx-tools
#8](LedFx/ledfx-tools#8). Follow-up
packet-wrapper cleanup, vendor lifecycle/frame ownership work and
preview subscription filtering/early frame coalescing are separate
post-merge changes.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Performance**
* Improved effect rendering, color conversion, and visualization
updates, including smoother handling of large matrices.
* **Device Output**
* Updated network device integrations for Art-Net, DDP, E1.31, OPC, OSC,
UDP, Nanoleaf, and other supported devices.
* Improved sender replacement and address-resolution handling to help
maintain output during configuration changes.
  * Fixed OpenRGB transfers to ensure complete frames are sent.
* **Reliability**
* Improved application startup and shutdown behavior, including tray
startup and CI smoke tests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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