Skip to content

SwiftQUIC: PERF: Move CongestionControl state to a common struct - #204

Open
agnosticdev wants to merge 1 commit into
mainfrom
agnosticdev/CongestionControl
Open

agnosticdev wants to merge 1 commit into
mainfrom
agnosticdev/CongestionControl

Conversation

@agnosticdev

Copy link
Copy Markdown
Collaborator

In recent flame graphs for the server we have noticed that findNewlyAckedPackets is a common hotspot when receiving packets for two reasons:

  1. The logic in finding the sent path is expensive.
  2. The logic in congestionControlPacketsAcked is also expensive due to exclusivity and copies that need to be made every time the enum algorithm alters itself.

For number 1 the sent path can be determined by matching the path identifier on the PacketContainerEntry.

For number 2 we can actually avoid some of the exclusivity and the copying costs by moving the congestion control logic inside a non-copyable struct. Next we can move the common state for all algorithms into a CongestionControlState struct and this is where the common state is now tracked and altered for all algorithms. This essentially shields any copies or writes from being done on QUICPath directly and allows packetsAcked and packetSent to make writes just on the state object and not on QUICPath. This prevents some exclusivity and copies on these two functions.

Net savings of about 430 megacycles:
Top of tree:

2.12 G   4.6%	101.98 M	 Recovery.InnerState.findNewlyAckedPackets(ackFrame:path:now:connection:)	
1.44 G   3.1%	5.20 M  	   Recovery.InnerState.packetAcked(sentPath:sentEntry:connection:)	
163.74 M 0.4%	125.91 M	   Recovery.InnerState.removeSentPacket(_:)
190.84 M 99.2%	1.00 M	       QUICPath.congestionControlPacketsAcked(bytesAcked:sentTime:)	

219.77 M 91.3%	1.00 M	       QUICPath.congestionControlPacketsSent(bytesSent:qlog:)	

With this change:

1.84 G 99.6%	99.03 M	     Recovery.InnerState.findNewlyAckedPackets(ackFrame:path:now:connection:in:) [inlined]	
1.32 G 71.5%	3.32 M	      Recovery.InnerState.packetAcked(sentPath:sentEntry:connection:in:) [inlined]	
58.39 M 3.2%	-	          QUICPath.congestionControlPacketsAcked(bytesAcked:sentTime:) [inlined]	

66.89 M 91.5%	1.20 M	      QUICPath.congestionControlPacketsSent(bytesSent:qlog:) [inlined]	

@agnosticdev
agnosticdev requested a review from rnro October 1, 2026 22:46
@agnosticdev agnosticdev added the 🔨 semver/patch No public API change. label Oct 1, 2026
@agnosticdev

Copy link
Copy Markdown
Collaborator Author

This change does not change any of the congestion control logic, it just moves the objects around.

self.algorithm = algorithm
}

static func createCubic(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer an init where we pass Algorithm and then we do a switch on it.

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

Labels

🔨 semver/patch No public API change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants