Skip to content

[codex] Improve high-event hot paths#1389

Draft
helto4real wants to merge 3 commits into
mainfrom
codex/latest-origin-main
Draft

[codex] Improve high-event hot paths#1389
helto4real wants to merge 3 commits into
mainfrom
codex/latest-origin-main

Conversation

@helto4real

Copy link
Copy Markdown
Collaborator

Breaking change

None. This keeps public APIs unchanged and preserves raw OnHassMessage publication behavior.

Proposed change

This PR implements the measured high-event performance improvements from the performance review plan.

The command/result hot path now uses an ID-indexed pending-result table instead of creating one filtered Rx subscription per command. HomeAssistantConnection registers a TaskCompletionSource<HassMessage> for each command id, completes matching result messages directly in the receive loop, and still publishes every raw HassMessage to OnHassMessage for ping, trigger, and diagnostic consumers.

The websocket receive path now reads a complete websocket message into a byte buffer, detects whether the payload is a JSON object or coalesced array, and deserializes directly to T or T[]. This removes the prior JsonElement intermediate and second typed deserialization on the ingest path while preserving chunked message and remote-close behavior.

The state-change benchmark harness now keeps the owning JsonDocument alive for the benchmark lifetime, fixing the previous invalid JsonElement baseline failure. The performance review document now includes the post-change results and report locations.

Performance report summary, comparing the local baseline commit d1eb2dd4 Add high-event performance benchmark baseline with the post-change full BenchmarkDotNet run:

Area Scenario Baseline Post-change Result
Result dispatch 1 pending command 215.94 ns, 880 B 44.92 ns, 352 B 79% faster, 60% lower allocation
Result dispatch 10 pending commands 2,081.51 ns, 6,424 B 326.47 ns, 1,360 B 84% faster, 79% lower allocation
Result dispatch 100 pending commands 29,398.81 ns, 133,144 B 3,282.14 ns, 12,688 B 89% faster, 90% lower allocation
Result dispatch 1000 pending commands 1,767,235.91 ns, 8,528,344 B 27,993.05 ns, 126,976 B 98% faster, 99% lower allocation
Websocket receive Single event read 6,638.9 ns, 7.2 KB 2,054.0 ns, 5,600 B 69% faster, lower allocation
Websocket receive 64 coalesced events 306,653.5 ns, 259.98 KB 111,851.9 ns, 291,785 B 64% faster, about 10% more allocation
State-change benchmark Lazy entity/new_state access previously failed with NA 31.28 ns, 48 B benchmark fixed and now measurable
State-change benchmark Force deserialize new_state 511.0 ns, 952 B 505.98 ns, 952 B effectively unchanged

Benchmark artifacts from the post-change run are local ignored build outputs under:

  • benchmarks/NetDaemon.PerformanceBenchmarks/bin/Release/net10.0/BenchmarkDotNet.Artifacts/results/NetDaemon.PerformanceBenchmarks.Benchmarks.ResultDispatchBenchmarks-report-github.md
  • benchmarks/NetDaemon.PerformanceBenchmarks/bin/Release/net10.0/BenchmarkDotNet.Artifacts/results/NetDaemon.PerformanceBenchmarks.Benchmarks.WebSocketPipelineBenchmarks-report-github.md
  • benchmarks/NetDaemon.PerformanceBenchmarks/bin/Release/net10.0/BenchmarkDotNet.Artifacts/results/NetDaemon.PerformanceBenchmarks.Benchmarks.StateChangeJsonBenchmarks-report-github.md
  • benchmarks/NetDaemon.PerformanceBenchmarks/bin/Release/net10.0/BenchmarkDotNet.Artifacts/results/NetDaemon.PerformanceBenchmarks.Benchmarks.QueuedObservableBenchmarks-report-github.md

The websocket coalesced-events path trades higher allocation for substantially lower latency in this benchmark. That is called out in the report so future receive-path tuning can target both.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (which adds functionality to an existing integration)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:

Detailed local report: docs/performance/high-event-review.md.

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • The code compiles without warnings (code quality check)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration are added/changed:

No user-facing functionality or configuration was added or changed.

@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.64286% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 82%. Comparing base (0116c98) to head (2d80d9a).

Files with missing lines Patch % Lines
...mon.HassClient/Internal/HomeAssistantConnection.cs 92% 1 Missing and 1 partial ⚠️
...sClient/Internal/Net/WebSocketTransportPipeline.cs 96% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@         Coverage Diff          @@
##           main   #1389   +/-   ##
====================================
  Coverage    82%     82%           
====================================
  Files       202     202           
  Lines      3941    3959   +18     
  Branches    444     451    +7     
====================================
+ Hits       3240    3257   +17     
- Misses      531     533    +2     
+ Partials    170     169    -1     
Flag Coverage Δ
unittests 82% <94%> (+<1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant