Simple optimizations for Deserializer performance - #67
Conversation
tfpauly
commented
Aug 5, 2026
- Avoid allocating scratchSpace up front
- Inline more functions
- Use an "empty" factory instead of a "single" span factory for the simple Frame case
agnosticdev
left a comment
There was a problem hiding this comment.
Thank you for this PR. I can appreciate that this keeps the Deserializer API altogether in one piece but I feel we still need more from the performance here.
Two things are affecting the performance:
- Generating the
bytesproperty each time we want to Deserialize. - Running this all in a closure eats up some CPU too.
One excellent example that comes to mind here is parsing a short header packet on a busy connection. On a busy connection there can be hundreds of thousands of packets and when parsing a short header packet we would need to parse the first octet on the packet to determine the packet type (short of long header) and then parse the connection ID. So that computes a bytes property for reading the octet and then again for reading the connection ID for each short header packet.
Approving to move the needle here but we cannot continue on with the CPU cost of this computed property. We need to come up with a solution here.
| guard isValid else { return nil } | ||
| switch buffer { | ||
| case .bytes: | ||
| return _bytes.span.extracting(startOffset..<(effectiveBufferLength - endOffset)).bytes |
There was a problem hiding this comment.
In my local benchmark for Deserializing 1 frame 10,000,000 times this generating this computed property accounts for 17% of the CPU used. In an ideal world we would not have to do this at all.
We can claw a back bit more CPU back from here if we use:
return _bytes.span.extracting(unchecked: startOffset..<(effectiveBufferLength - endOffset)).bytes
| @@ -761,30 +789,36 @@ public struct Deserializer<Factory: DeserializerSpanFactory & ~Copyable & ~Escap | |||
| } | |||
|
|
|||
| public static func deserialize( | |||
There was a problem hiding this comment.
We pay a tax here having this all done through a closure, we can claw back a little if inline this function.