perf: serialize identical broadcasts once (deferred — invisible at friends scale) #52

Open
opened 2026-07-19 00:48:44 +02:00 by marco · 1 comment
Owner

From the code-review performance pass (its only actionable item). WebSocketConnection.SendAsync serializes the message per call, so an identical broadcast to N nearby listeners (Swing / Attacked / PlayerDied / PlayerResurrected / PlayerSaid) is serialized + allocated N times.

Deferred deliberately: at the target scale (tens of players clustered) this is microseconds and negligible Gen0 garbage — the perf reviewer flagged it as the one worth doing eventually but explicitly invisible now, and it adds send-path plumbing (a pre-serialized-bytes path on IClientConnection + the test fake). Per the project's right-size-performance principle, not worth the churn yet.

When to do it: if player counts or broadcast frequency ever grow enough to show GC pressure.

Scope (when picked up)

  • Add a ReadOnlyMemory<byte> send path to IClientConnection/WebSocketConnection (enqueue pre-serialized bytes).
  • ConnectionManager.SendToManyAsync(ids, message) serializes once, sends the same buffer to all.
  • Point the identical-broadcast loops (GameLoopService swings/attacks/deaths/resurrections, GameSessionHandler PlayerSaid) at it. (Per-observer reconcile sends stay per-message — they differ.)

Definition of Done

  • An identical broadcast to N listeners serializes the payload exactly once (verifiable by a unit test counting serializations, or by inspection).
  • Whole suite green, 0 warnings.
From the code-review performance pass (its only actionable item). `WebSocketConnection.SendAsync` serializes the message per call, so an identical broadcast to N nearby listeners (Swing / Attacked / PlayerDied / PlayerResurrected / PlayerSaid) is serialized + allocated N times. **Deferred deliberately**: at the target scale (tens of players clustered) this is microseconds and negligible Gen0 garbage — the perf reviewer flagged it as the one worth doing *eventually* but explicitly invisible now, and it adds send-path plumbing (a pre-serialized-bytes path on `IClientConnection` + the test fake). Per the project's right-size-performance principle, not worth the churn yet. **When to do it**: if player counts or broadcast frequency ever grow enough to show GC pressure. ## Scope (when picked up) - Add a `ReadOnlyMemory<byte>` send path to `IClientConnection`/`WebSocketConnection` (enqueue pre-serialized bytes). - `ConnectionManager.SendToManyAsync(ids, message)` serializes once, sends the same buffer to all. - Point the identical-broadcast loops (GameLoopService swings/attacks/deaths/resurrections, GameSessionHandler PlayerSaid) at it. (Per-observer reconcile sends stay per-message — they differ.) ## Definition of Done - [ ] An identical broadcast to N listeners serializes the payload exactly once (verifiable by a unit test counting serializations, or by inspection). - [ ] Whole suite green, 0 warnings.
Author
Owner

Reconfirmed by this session's critical performance review: at friends scale this is genuinely fine-as-is (a handful of observers = a handful of tiny JSON encodes per event, GC-trivial at a 100ms tick). The DTO object graph is already built once per event; only the final JSON+UTF8 encoding is per-recipient. Fix path when it ever matters: encode the frame to a byte[] once in WorldTick and hand the pre-encoded bytes to each connection (a SendRaw(byte[]) path). Staying deferred.

Reconfirmed by this session's critical performance review: at friends scale this is genuinely fine-as-is (a handful of observers = a handful of tiny JSON encodes per event, GC-trivial at a 100ms tick). The DTO object graph is already built once per event; only the final JSON+UTF8 encoding is per-recipient. Fix path when it ever matters: encode the frame to a byte[] once in WorldTick and hand the pre-encoded bytes to each connection (a SendRaw(byte[]) path). Staying deferred.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#52
No description provided.