Skip to content

perf: drop the live WebSocket feed from 2 Hz to 1 Hz - #1929

Closed
padddi wants to merge 9 commits into
bitaxeorg:v2.15.xfrom
padddi:perf/websocket-feed-rate
Closed

perf: drop the live WebSocket feed from 2 Hz to 1 Hz#1929
padddi wants to merge 9 commits into
bitaxeorg:v2.15.xfrom
padddi:perf/websocket-feed-rate

Conversation

@padddi

@padddi padddi commented Aug 30, 2026

Copy link
Copy Markdown

Problem

Every tick of websocket_api_task() rebuilds the full system-info JSON tree via system_api_get_full_json(), then diffs it against the previous one with cJSON_GetDiff().

cJSON objects are linked lists, so the cJSON_GetObjectItemCaseSensitive() lookup inside the diff loop is a linear scan per key. Over the tree's roughly 100 keys that is on the order of 5,000 string comparisons per tick — and at 500 ms, 10,000 per second. It is the largest recurring CPU cost on the device while the web UI is open, and it runs in full even when nothing has changed.

Change

One line: the rate limit goes from 500 ms to 1000 ms.

None of the telemetry the feed carries — hashrate averages, temperatures, fan RPM, share counts — changes faster than once a second, so the higher rate bought nothing. This halves the cost with no structural risk.

Frontend

No change needed. live-data.service.ts merges whatever arrives and makes no assumption about the interval.

Scope

This changes how often the diff runs, not the diff itself. Replacing the O(n²) comparison is a separate change, and there are two reasonable approaches:

  • Keep the last sent state in a flat struct and compare field by field — O(n) over fixed offsets, and it drops the second cJSON tree entirely.
  • Walk both objects in parallel. The key order is deterministic because both come from system_api_get_full_json(), so a forward scan with a fallback gets O(n) for roughly 15 lines of change.

I'd suggest measuring before picking one. CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is already enabled in sdkconfig.defaults and task_monitor_task already exists, so a before/after capture is nearly free.

Testing

Not compiled — no ESP-IDF toolchain was available in the environment this was prepared in, so CI is the first real build. The change is a single constant.

benjamin-wilson and others added 8 commits August 22, 2026 18:33
* Make Wi-Fi PMS optional

* Disable experimental RSNO client support

* Add comment
- Serialize fallback API polling, relax WebSocket timeout and retry timing,
and enable HTTPD TCP keepalive to improve recovery from stale clients.
Spelling fix on settings page of web UI.
* Add Naja Duo 1201 board support (#1892)

* Add Naja Duo LCD interface (#1893)

* Add Naja Duo factory self-test (#1891)

* Add Gamma Hex 1300 board support (#1894)

* Review code (#1836)

* Mitigate VLA stack risk and fix read register delay (#1836)

* Use device config asic count for register loop (#1836)

* Extract constants (#1836)

* TPS546 PMBus Telemetry & Active-High ASIC Enable (#1837)

* DeviceConfig GPIO Pin Abstraction (#1840)

* Refactor device, family, and ASIC checks into declarative config

* Rename device pins to BITAXE_ORIGINAL_PINS and BITAXE_COLOR_PINS

* Cleanup whitespace in TPS546.h

* Use typed GlobalState in asic read_registers

* Add VLA stack guard

* Add domain_hashrate_scale to AsicConfig

* Fix vTaskDelay and add TODO tag on BM1373_read_registers

* Remove unused things

* Clean up asic_init_with_timings

* Document BM1373 baud setting location

* Clean up unused set_default_baud functions

* Fix BM1397 packet length guard

* fix display rotation (#12)

Co-authored-by: WantClue <wantclue@users.noreply.github.com>

* Fix BAP define

* Set protocol_task stack to 8kb

* Put display buffers in PSRAM

* Allocate valid_jobs in PSRAM

---------

Co-authored-by: Ben <wilsob12@gmail.com>
Co-authored-by: WantClue <info@wantclue.de>
Co-authored-by: WantClue <wantclue@users.noreply.github.com>
Every tick of websocket_api_task() rebuilds the full system-info JSON
tree via system_api_get_full_json(), then diffs it against the previous
one with cJSON_GetDiff(). cJSON objects are linked lists, so the
cJSON_GetObjectItemCaseSensitive() call inside the diff loop is a linear
scan per key: roughly 5,000 string comparisons over the tree's ~100
keys, per tick. At 500 ms that ran 10,000 times a second, and it is the
largest recurring CPU cost on the device while the web UI is open.

None of the telemetry the feed carries changes faster than once a
second, so the higher rate bought nothing. Halving it is a one-line
change with no structural risk.

The Angular client merges whatever arrives (live-data.service.ts) and
makes no assumption about the interval, so nothing on the frontend needs
to change.

This does not fix the underlying O(n^2) diff, only how often it runs.
Replacing it — keeping the last sent state in a flat struct and
comparing field by field, or walking both objects in parallel since the
key order is deterministic — is a separate change.

Not compiled: no ESP-IDF toolchain available in the environment this was
prepared in.
@padddi
padddi force-pushed the perf/websocket-feed-rate branch from c08fc58 to 3422072 Compare August 30, 2026 07:54
@padddi padddi changed the title Perf/websocket feed rate perf: drop the live WebSocket feed from 2 Hz to 1 Hz Aug 30, 2026
@0xf0xx0

0xf0xx0 commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

nack, ai generated + the impact is negligible, cpu usage stays below 15%

please don't open automated prs

@0xf0xx0 0xf0xx0 closed this Aug 30, 2026
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.

9 participants