You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Callers of SetRawData: TelemetryClient.cs:371 (UDP: fresh array from EndReceive), TelemetryClient.cs:811 (TCP replay: fresh array via .ToArray()), TelemetryClient.ReceiveDataFromFile:299 (array supplied by external caller), F1ReplayClient/MainWindow.xaml.cs:385
Problem
Every datagram is allocated twice (once by EndReceive, once by the defensive copy in SetRawData) and copied once — at 300-500 packets/s roughly 1 MB/s of avoidable Gen0 garbage, plus per-call IAsyncResult state from the APM pattern. Enum.ToObject adds one boxing allocation per packet.
Step-by-step fix (do steps 1-2; step 3 only with measurement)
Remove the defensive copy: change SetRawData(byte[] rawData) to take ownership of the passed array (_rawData = rawData;). Document the ownership contract in the XML <summary> ("the caller must not modify the array afterwards"). All four current callers pass freshly allocated arrays that they never reuse, so behavior is unchanged — verify each caller and state that in the PR. This also resolves the open question in F-043 · SetRawData allocates a new array per packet and copies, even though the callers (TelemetryClient.cs:811, … #39.
Remove the boxing: replace with a direct cast, e.g. PacketHeader.PacketType = (PacketTypes)(packetType + 1); (identical semantics; optionally validate the value range first).
Optional (measure first): replace the BeginReceive/EndReceive loop with an async Socket.ReceiveFromAsync loop using ArrayPool<byte> buffers. Only do this if allocation profiling after steps 1-2 still shows the receive path as significant. Warning: pooled buffers require explicit lifetime rules because ReceivedPacketData flows into two consumers (processing + file logging) — the buffer may only be returned after both are done. If in doubt, skip step 3 and note it.
Test guidance
Existing parsing tests cover SetRawData consumers. Add one test asserting PacketRawData returns the exact bytes passed in and that PacketHeader is parsed identically to before (compare against a known sample from F1Server.Tests/SampleData).
Acceptance criteria
At most one buffer allocation per received datagram on the default path
No boxing in the header parse
Ownership contract documented on SetRawData; all callers verified and listed in the PR
Full rebuild shows 0 Reihitsu (RH####) warnings
Related:#39 (covers the SetRawData copy — close it as resolved by this issue or link the PR to both).
Finding: PERF-12 (Medium) + PERF-20 (Low) · Area: F1Server.Service / F1Server.Core
Source:
docs/concepts/udp-packet-processing-performance-review.md(branchclaude/udp-packet-processing-review-798u7o), sections 2.2 and 2.4Files involved
F1Server.Core/Data/ReceivedPacketData.cs:76-87—SetRawDataallocates a second array and copiesF1Server.Core/Data/ReceivedPacketData.cs:147—(PacketTypes)Enum.ToObject(typeof(PacketTypes), packetType + 1)boxes per packetF1Server.Service/TelemetryClient.cs:361-371, 398— APM receive loop (BeginReceive/EndReceive)SetRawData:TelemetryClient.cs:371(UDP: fresh array fromEndReceive),TelemetryClient.cs:811(TCP replay: fresh array via.ToArray()),TelemetryClient.ReceiveDataFromFile:299(array supplied by external caller),F1ReplayClient/MainWindow.xaml.cs:385Problem
Every datagram is allocated twice (once by
EndReceive, once by the defensive copy inSetRawData) and copied once — at 300-500 packets/s roughly 1 MB/s of avoidable Gen0 garbage, plus per-callIAsyncResultstate from the APM pattern.Enum.ToObjectadds one boxing allocation per packet.Step-by-step fix (do steps 1-2; step 3 only with measurement)
SetRawData(byte[] rawData)to take ownership of the passed array (_rawData = rawData;). Document the ownership contract in the XML<summary>("the caller must not modify the array afterwards"). All four current callers pass freshly allocated arrays that they never reuse, so behavior is unchanged — verify each caller and state that in the PR. This also resolves the open question in F-043 · SetRawData allocates a new array per packet and copies, even though the callers (TelemetryClient.cs:811, … #39.PacketHeader.PacketType = (PacketTypes)(packetType + 1);(identical semantics; optionally validate the value range first).BeginReceive/EndReceiveloop with an asyncSocket.ReceiveFromAsyncloop usingArrayPool<byte>buffers. Only do this if allocation profiling after steps 1-2 still shows the receive path as significant. Warning: pooled buffers require explicit lifetime rules becauseReceivedPacketDataflows into two consumers (processing + file logging) — the buffer may only be returned after both are done. If in doubt, skip step 3 and note it.Test guidance
Existing parsing tests cover
SetRawDataconsumers. Add one test assertingPacketRawDatareturns the exact bytes passed in and thatPacketHeaderis parsed identically to before (compare against a known sample fromF1Server.Tests/SampleData).Acceptance criteria
SetRawData; all callers verified and listed in the PRRH####) warningsRelated: #39 (covers the
SetRawDatacopy — close it as resolved by this issue or link the PR to both).