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
Parsing QUIC headers with Deserializer.deserialize uses a lot more CPU than expected for what should be a very fast operation. Parsing the firstOctet and short headers are by far the common cases when parsing QUIC packets, and the paths that should be the most performant. This change is to provide a fast-path option for these operations and cut the CPU usage here by 63% when parsing QUIC headers in the QUICTransfer benchmark.
Savings of about 260 megacycles.
Current top of tree:
417.04 M 52.8% - PacketParser.parseHeader(frame:dcidLength:)
417.04 M 52.8% 30.48 M specialized PacketParser.parseHeader(frame:dcidLength:)
153.60 M 19.5% 12.00 M PacketParser.parseShortHeader(frame:firstOctet:dcidLength:originalLength:)
107.87 M 13.7% 5.00 M static Deserializer<>.deserialize<>(_:claim:_:)
41.72 M 5.3% 5.00 M Frame.claim(fromStart:fromEnd:adjustSingleIPAggregate:)
27.11 M 3.4% 27.11 M 0x192be0ce8 (libsystem_pthread.dylib +0x1ce9) <56F332B8-6B78-3DB7-A58C-F3F4EC03D383>
5.00 M 0.6% 5.00 M type metadata accessor for Logger
2.60 M 0.3% 2.60 M Frame.startOffset.setter
2.00 M 0.3% 2.00 M DYLD-STUB$$type metadata accessor for Logger
38.15 M 4.8% - static Deserializer<>.deserialize<>(_:_:)
23.00 M 2.9% 22.00 M Frame.bytes.getter
26.73 M 3.4% - QUICConnectionID.init(storage:size:)
26.73 M 3.4% 9.13 M specialized QUICConnectionID.init(storage:size:)
7.00 M 0.9% - Packet.init(destinationConnectionID:headerLength:spin:)
133.11 M 16.9% - specialized UniqueDeque<>.reserveCapacity(_:)
133.11 M 16.9% - specialized RigidDeque<>.reserveCapacity(_:)
133.11 M 16.9% - specialized RigidDeque<>.reallocate(capacity:)
133.11 M 16.9% 4.57 M specialized _UnsafeDequeHandle<>.reallocate(capacity:)
99.84 M 12.6% 10.00 M static Deserializer<>.deserialize<>(_:claim:_:)
50.82 M 6.4% 9.00 M Frame.claim(fromStart:fromEnd:adjustSingleIPAggregate:)
34.82 M 4.4% 34.82 M 0x192be0ce8 (libsystem_pthread.dylib +0x1ce9) <56F332B8-6B78-3DB7-A58C-F3F4EC03D383>
4.00 M 0.5% 4.00 M Frame.startOffset.setter
3.00 M 0.4% 3.00 M DYLD-STUB$$type metadata accessor for Logger
23.00 M 2.9% 22.00 M Frame.bytes.getter
1.00 M 0.1% - specialized UniqueArray<>.span.getter
12.01 M 1.5% - static Deserializer<>.deserialize<>(_:_:)
12.01 M 1.5% 3.01 M specialized static Deserializer<>.deserialize(_:_:)
4.00 M 0.5% 4.00 M type metadata accessor for Logger
And with this change:
158.20 M 47.8% 20.74 M specialized PacketParser.parseHeader(frame:dcidLength:)
120.46 M 36.4% 149.52 k PacketParser.parseShortHeader(frame:firstOctet:dcidLength:originalLength:)
120.31 M 36.3% 50.91 M specialized PacketParser.parseShortHeader(frame:firstOctet:dcidLength:originalLength:)
44.43 M 13.4% 11.91 M Frame.copyInto(inlineArray:length:)
9.00 M 2.7% 9.00 M Frame.unclaimedLength.getter
7.00 M 2.1% - static InlineArray<>.empty.getter
5.00 M 1.5% 5.00 M Frame.claim(fromStart:fromEnd:adjustSingleIPAggregate:)
3.97 M 1.2% - Packet.init(destinationConnectionID:headerLength:spin:)
16.00 M 4.8% - Frame.firstOctet.getter
1.00 M 0.3% - Frame.claim(fromStart:fromEnd:adjustSingleIPAggregate:)
Is there an approach we can use to make the actual deserialize operation cheaper here, as opposed to avoiding using it?
I created a new type called InlineDeserializer that extracts bytes from the Frame's NetworkUniqueArray<UInt8>, this is the most performant way to extract bytes from the frame. This should get us where we want to be CPU-wise.
Its also worth mentioning why I created a new type here (InlineDeserializer) instead of using the Deserializer type directly. The Deserializer type loads the Span<UInt8> from a frame into the Span of the Deserializer. Then when values are extracted from the Deserializer it reads them from this span. This path uses too much CPU. Extracting the bytes directly from the NetworkUniqueArray<UInt8> is by far the more performant route. After this PR I will plan to move more of our data path packet parsing to use this technique.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Parsing QUIC headers with
Deserializer.deserializeuses a lot more CPU than expected for what should be a very fast operation. Parsing the firstOctet and short headers are by far the common cases when parsing QUIC packets, and the paths that should be the most performant. This change is to provide a fast-path option for these operations and cut the CPU usage here by 63% when parsing QUIC headers in the QUICTransfer benchmark.Savings of about 260 megacycles.
Current top of tree:
And with this change: