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
Add a package for slices utilities #25069
Add a package for slices utilities #25069
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot ✔️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️ this. Nice work! A few comments about benchmarks results below.
Also, did you perform an exhaustive scan on the rest of the code for other potential places where we'd need to convert them over to the new API?
pkg/slices/slices_test.go
Outdated
// name old time/op new time/op delta | ||
// Unique/96-8 3.17µs ± 9% 4.83µs ±15% +52.50% (p=0.008 n=5+5) | ||
// Unique/128-8 4.97µs ± 5% 5.95µs ± 2% +19.83% (p=0.008 n=5+5) | ||
// Unique/160-8 7.20µs ±12% 7.33µs ± 1% ~ (p=0.690 n=5+5) | ||
// Unique/192-8 9.29µs ± 3% 9.07µs ± 2% ~ (p=0.151 n=5+5) | ||
// Unique/256-8 15.4µs ± 4% 11.2µs ± 2% -27.56% (p=0.008 n=5+5) | ||
// ... | ||
// | ||
// After 192 elements, the map based approach becomes more efficient. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you include results for memory just so we can get an overall sense of the performance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added memory results, too.
I pointed out in the comment that, since the number of allocations from the loop algorithm is always 0, benchstat reports +Inf
when comparing the two. But the info in the previous two columns (old alloc/op and new alloc/op) should be enough to highlight the allocations performed by the map approach:
...
name old alloc/op new alloc/op delta
Unique/96-8 0.00B 1474.00B ± 2% +Inf% (p=0.008 n=5+5)
Unique/128-8 0.00B 3100.00B ± 0% +Inf% (p=0.008 n=5+5)
Unique/160-8 0.00B 3113.20B ± 0% +Inf% (p=0.008 n=5+5)
Unique/192-8 0.00B 3143.20B ± 0% +Inf% (p=0.008 n=5+5)
Unique/256-8 0.00B 6178.00B ± 0% +Inf% (p=0.008 n=5+5)
name old allocs/op new allocs/op delta
Unique/96-8 0.00 3.20 ±38% +Inf% (p=0.008 n=5+5)
Unique/128-8 0.00 2.00 ± 0% +Inf% (p=0.008 n=5+5)
Unique/160-8 0.00 3.00 ± 0% +Inf% (p=0.016 n=5+4)
Unique/192-8 0.00 4.00 ± 0% +Inf% (p=0.008 n=5+5)
Unique/256-8 0.00 2.00 ± 0% +Inf% (p=0.008 n=5+5)
func (s *IPTestSuite) BenchmarkKeepUniqueIPs(c *C) { | ||
size := 1000 | ||
ipsOrig := make([]net.IP, 0, size) | ||
for i := 0; i < size; i++ { | ||
ipsOrig = append(ipsOrig, net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i>>0))) | ||
} | ||
ips := make([]net.IP, 0, len(ipsOrig)) | ||
|
||
copy(ips, ipsOrig) | ||
for i := 0; i < c.N; i++ { | ||
c.StopTimer() | ||
rand.Shuffle(len(ips), func(i, j int) { | ||
ips[i], ips[j] = ips[j], ips[i] | ||
}) | ||
c.StartTimer() | ||
|
||
KeepUniqueIPs(ips) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually curious to see what the difference in benchmark results were between the old KeepUniqueIPs
and the new SortedUniqueFunc
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here they are (I used the new benchmark to compare the old and the new function to see the differences while scaling the slice size):
name old time/op new time/op delta
KeepUniqueIPs/96-8 17.2µs ±34% 15.6µs ±29% ~ (p=0.421 n=5+5)
KeepUniqueIPs/128-8 24.5µs ± 5% 23.8µs ± 3% ~ (p=0.151 n=5+5)
KeepUniqueIPs/160-8 31.2µs ± 8% 29.8µs ± 2% -4.47% (p=0.032 n=5+5)
KeepUniqueIPs/192-8 37.2µs ± 4% 36.2µs ± 2% ~ (p=0.056 n=5+5)
KeepUniqueIPs/256-8 50.6µs ± 1% 48.6µs ± 0% -4.04% (p=0.008 n=5+5)
KeepUniqueIPs/512-8 108µs ± 3% 105µs ± 1% -3.27% (p=0.008 n=5+5)
KeepUniqueIPs/1024-8 235µs ± 1% 225µs ± 0% -4.26% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
KeepUniqueIPs/96-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
KeepUniqueIPs/128-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
KeepUniqueIPs/160-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
KeepUniqueIPs/192-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
KeepUniqueIPs/256-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
KeepUniqueIPs/512-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
KeepUniqueIPs/1024-8 96.0B ± 0% 96.0B ± 0% ~ (all equal)
name old allocs/op new allocs/op delta
KeepUniqueIPs/96-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
KeepUniqueIPs/128-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
KeepUniqueIPs/160-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
KeepUniqueIPs/192-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
KeepUniqueIPs/256-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
KeepUniqueIPs/512-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
KeepUniqueIPs/1024-8 3.00 ± 0% 3.00 ± 0% ~ (all equal)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks. It would be great to have them in the commit msg IMO
/test Job 'Cilium-PR-K8s-1.27-kernel-net-next' failed: Click to show.Test Name
Failure Output
Jenkins URL: https://jenkins.cilium.io/job/Cilium-PR-K8s-1.27-kernel-net-next/147/ If it is a flake and a GitHub issue doesn't already exist to track it, comment Then please upload the Jenkins artifacts to that issue. |
29eca2e
to
2a003ec
Compare
Force-pushed to address #25069 (comment), only the test comment has been changed: // Unique/160-8 7.20µs ±12% 7.33µs ± 1% ~ (p=0.690 n=5+5)
// Unique/192-8 9.29µs ± 3% 9.07µs ± 2% ~ (p=0.151 n=5+5)
// Unique/256-8 15.4µs ± 4% 11.2µs ± 2% -27.56% (p=0.008 n=5+5)
-// ...
+
+// name old alloc/op new alloc/op delta
+// Unique/96-8 0.00B 1474.00B ± 2% +Inf% (p=0.008 n=5+5)
+// Unique/128-8 0.00B 3100.00B ± 0% +Inf% (p=0.008 n=5+5)
+// Unique/160-8 0.00B 3113.20B ± 0% +Inf% (p=0.008 n=5+5)
+// Unique/192-8 0.00B 3143.20B ± 0% +Inf% (p=0.008 n=5+5)
+// Unique/256-8 0.00B 6178.00B ± 0% +Inf% (p=0.008 n=5+5)
+
+// name old allocs/op new allocs/op delta
+// Unique/96-8 0.00 3.20 ±38% +Inf% (p=0.008 n=5+5)
+// Unique/128-8 0.00 2.00 ± 0% +Inf% (p=0.008 n=5+5)
+// Unique/160-8 0.00 3.00 ± 0% +Inf% (p=0.016 n=5+4)
+// Unique/192-8 0.00 4.00 ± 0% +Inf% (p=0.008 n=5+5)
+// Unique/256-8 0.00 2.00 ± 0% +Inf% (p=0.008 n=5+5)
//
// After 192 elements, the map based approach becomes more efficient.
+// Regarding the memory, the number of allocations for the double loop algorithm is always 0,
+// that's why benchstat is reporting "+Inf%" in the delta column.
+// The relevant differences between the two approaches in terms of memory are shown in the previous
+// two columns.
func BenchmarkUnique(b *testing.B) {
var benchCases = [...]int{96, 128, 160, 192, 256, 512, 1024} |
After another scan I found several other potential candidates:
I think I can convert these in one or more follow up PRs. WDYT? |
Sounds reasonable. |
2a003ec
to
8f90050
Compare
Force-pushed to add the benchmark results to the commit message as suggested by #25069 (comment). |
Add a package with common utilities to work on slices of any type. Currently, functions to deduplicate and deduplicate with sorting are added. Signed-off-by: Fabio Falzoi <fabio.falzoi@isovalent.com>
Change KeepUnique* functions to wrap `slices.SortedUniqueFunc` from pkg/slices. The tests are kept since they are useful to verify the logic of the custom less and eq functions passed to `slices.SortedUniqueFunc`. Instead, the old benchmark for KeepUniqueIPs is removed in favor of the one already present in pkg/slices that fulfill the same purpose. Here the results after running the new benchmark against the old version and the new one of KeepUniqueIPs: name old time/op new time/op delta KeepUniqueIPs/96-8 17.2µs ±34% 15.6µs ±29% ~ (p=0.421 n=5+5) KeepUniqueIPs/128-8 24.5µs ± 5% 23.8µs ± 3% ~ (p=0.151 n=5+5) KeepUniqueIPs/160-8 31.2µs ± 8% 29.8µs ± 2% -4.47% (p=0.032 n=5+5) KeepUniqueIPs/192-8 37.2µs ± 4% 36.2µs ± 2% ~ (p=0.056 n=5+5) KeepUniqueIPs/256-8 50.6µs ± 1% 48.6µs ± 0% -4.04% (p=0.008 n=5+5) KeepUniqueIPs/512-8 108µs ± 3% 105µs ± 1% -3.27% (p=0.008 n=5+5) KeepUniqueIPs/1024-8 235µs ± 1% 225µs ± 0% -4.26% (p=0.008 n=5+5) name old alloc/op new alloc/op delta KeepUniqueIPs/96-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) KeepUniqueIPs/128-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) KeepUniqueIPs/160-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) KeepUniqueIPs/192-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) KeepUniqueIPs/256-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) KeepUniqueIPs/512-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) KeepUniqueIPs/1024-8 96.0B ± 0% 96.0B ± 0% ~ (all equal) name old allocs/op new allocs/op delta KeepUniqueIPs/96-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) KeepUniqueIPs/128-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) KeepUniqueIPs/160-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) KeepUniqueIPs/192-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) KeepUniqueIPs/256-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) KeepUniqueIPs/512-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) KeepUniqueIPs/1024-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) Signed-off-by: Fabio Falzoi <fabio.falzoi@isovalent.com>
Use helpers from pkg/slices to deduplicate names slice. Remove the old test and benchmark for the no more used KeepUniqueNames. Signed-off-by: Fabio Falzoi <fabio.falzoi@isovalent.com>
Avoid the addition of package level code and instead rely on the common helpers available in pkg/slices. Signed-off-by: Fabio Falzoi <fabio.falzoi@isovalent.com>
8f90050
to
e3d9832
Compare
/test Job 'Cilium-PR-K8s-1.26-kernel-net-next' failed: Click to show.Test Name
Failure Output
Jenkins URL: https://jenkins.cilium.io/job/Cilium-PR-K8s-1.26-kernel-net-next/1944/ If it is a flake and a GitHub issue doesn't already exist to track it, comment Then please upload the Jenkins artifacts to that issue. |
/mlh new-flake Cilium-PR-K8s-1.26-kernel-net-next 👍 created #25163 |
/test-1.26-net-next |
Add a package with utilities to deduplicate and deduplicate-and-sort slices of any type.
Currently, the Cilium codebase uses multiple (but very similar) approaches to deduplicate slices in different packages, like in
pkg/ip
,pkg/fqdn
andoperator/pkg/model/translation
.The PR adds a separate package to be used throughout the codebase for deduplicate elements in a slice, with 3 different functions that should cover all the current needs:
Unique
: deduplication of a slice of any comparable type, preserving the ordering and modifying the slice in place.SortedUnique
: sorting and deduplication of a slice ofconstraints.Ordered
type elements, operating in place.SortedUniqueFunc
: likeSortedUnique
, but usable also on composite types and/or with user-customized comparison logic.The
Unique
function uses an optimization for small slices inspired by previous functionKeepUniqueNames
frompkg/fqdn
.