Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer packet ordering until building RTCP packet #202

Merged
merged 1 commit into from
Aug 30, 2023

Conversation

treyhakanson
Copy link
Contributor

Description

The previous implementation did a sorted insert (O(m)) on every call to Record. If sorting is deferred until a feedback packet is built, we can record in constant time and build a packet in O(nlogn). Ordering isn't required until building the packet anyway, and deferring nets a minor performance gain (I say minor since its unlikely there are a large number of received packets in the buffer prior to building a feedback packet). If ordering is really needed on record, we could use something like a B-tree to get O(logn) sorted inserts.

Reference issue

N/A

@codecov
Copy link

codecov bot commented Aug 28, 2023

Codecov Report

Patch coverage: 76.92% and project coverage change: -0.36% ⚠️

Comparison is base (c37a592) 79.51% compared to head (a646d66) 79.16%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #202      +/-   ##
==========================================
- Coverage   79.51%   79.16%   -0.36%     
==========================================
  Files          67       67              
  Lines        3369     3365       -4     
==========================================
- Hits         2679     2664      -15     
- Misses        572      580       +8     
- Partials      118      121       +3     
Flag Coverage Δ
go 79.16% <76.92%> (-0.36%) ⬇️
wasm 77.17% <76.92%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Changed Coverage Δ
pkg/twcc/twcc.go 94.88% <76.92%> (-0.12%) ⬇️

... and 3 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@boks1971 boks1971 left a comment

Choose a reason for hiding this comment

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

lgtm @treyhakanson .

Let me request more 👀 on this.

pkg/twcc/twcc.go Outdated
@@ -19,7 +20,7 @@ type pktInfo struct {
// transport wide congestion control feedback reports as specified in
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
type Recorder struct {
receivedPackets []pktInfo
receivedPackets map[uint32]pktInfo
Copy link
Contributor

@boks1971 boks1971 Aug 28, 2023

Choose a reason for hiding this comment

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

Based on very limited golang knowledge, maps seem to incur more memory (expected, but making the comment in the sense I have been surprised some times maps growing unexpectedly). Is there a need to use map here?

Also, this is constructing the slice from map in building feedback packet. That can be avoided I think if we just append and sort that slice directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@boks1971 thanks for the comment! I used a map here to handle the need to de-dupe packets with the same sequence number (see the previous insertSorted method). I could use a slice and de-dupe during iteration when building the feedback packet, but this would require a map/set anyway

Copy link
Contributor

@boks1971 boks1971 Aug 28, 2023

Choose a reason for hiding this comment

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

Thank you @treyhakanson . As these maps should be small, it should be fine. On that, one comment, I think re-allocating the map after building report would be needed to ensure that map does not grow. Did not see it. Maybe, I missed it in the diff.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can do the deduplication when building the feedback packet without a map. When iterating over the sorted slice, you remember the previous value and compare it to the next because in the sorted slice, all duplicates will be neighbors. It could be somewhat fiddly, depending on whether you want to insert the first or last occurrence. Currently, we take the last (which would be more complicated), but I think we may want to use the first instead (ordered by arrival time, though, not by the time it was added). That could be added as a criterion to the sort function, then it would be easy again (just check if the current sequence number was already added in the last iteration). However, all this is probably very hypothetical since there will rarely be duplicates anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah you're totally right, not sure why I didn't think of that; thanks for calling out!
Updated to implement this approach and take the packet with the earliest arrival time. I also updated TestDuplicatePackets to test that we take the packet with the earliest arrival time even if it isn't the first one we record. Fwiw, I've been testing on LTE/5G networks recently and have noticed a fair bit of duplication so its seems worth handling

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, that's good to know, thanks!
And thanks for implementing this change!

pkg/twcc/twcc.go Outdated
feedback = newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt)
r.fbPktCnt++
feedback.addReceived(uint16(pkt.sequenceNumber&0xffff), pkt.arrivalTime)
}
}
r.receivedPackets = []pktInfo{}
pkts = append(pkts, feedback.getRTCP())
r.receivedPackets = make(map[uint32]pktInfo)
Copy link
Contributor

@boks1971 boks1971 Aug 28, 2023

Choose a reason for hiding this comment

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

Ignore my comment above. See it now. Sorry, my bad.

@treyhakanson
Copy link
Contributor Author

For more context on the motivation behind this change, see this discussion on the pion slack channel: https://gophers.slack.com/archives/CAK2124AG/p1692822880632899

pkg/twcc/twcc.go Outdated
@@ -19,7 +20,7 @@ type pktInfo struct {
// transport wide congestion control feedback reports as specified in
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
type Recorder struct {
receivedPackets []pktInfo
receivedPackets map[uint32]pktInfo
Copy link
Contributor

Choose a reason for hiding this comment

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

You can do the deduplication when building the feedback packet without a map. When iterating over the sorted slice, you remember the previous value and compare it to the next because in the sorted slice, all duplicates will be neighbors. It could be somewhat fiddly, depending on whether you want to insert the first or last occurrence. Currently, we take the last (which would be more complicated), but I think we may want to use the first instead (ordered by arrival time, though, not by the time it was added). That could be added as a criterion to the sort function, then it would be easy again (just check if the current sequence number was already added in the last iteration). However, all this is probably very hypothetical since there will rarely be duplicates anyway.

The previous implementation did a sorted insert (O(m)) on
every call to `Record`. If sorting is deferred until a
feedback packet is built, we can record in constant time
and build a packet in O(nlogn). Ordering isn't required
until building the packet anyway, and deferring nets a minor
performance gain (I say minor since its unlikely there are a
large number of received packets in the buffer prior to
building a feedback packet). If ordering is really needed on
record, we could use something like a B-tree to get O(logn)
sorted inserts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants