diff --git a/protocol/lib/collections.go b/protocol/lib/collections.go index 52d8baf794..94ffc732e6 100644 --- a/protocol/lib/collections.go +++ b/protocol/lib/collections.go @@ -7,13 +7,16 @@ import ( // ContainsDuplicates returns true if the slice contains duplicates, false if not. func ContainsDuplicates[V comparable](values []V) bool { - seenValues := make(map[V]bool) - for _, val := range values { - if _, exists := seenValues[val]; exists { + // Store each value as a key in the mapping. + seenValues := make(map[V]struct{}, len(values)) + for i, val := range values { + // Add the value to the mapping. + seenValues[val] = struct{}{} + + // Return early if the size of the mapping did not grow. + if len(seenValues) <= i { return true } - - seenValues[val] = true } return false diff --git a/protocol/lib/collections_test.go b/protocol/lib/collections_test.go index 864c5e49d9..7788f46222 100644 --- a/protocol/lib/collections_test.go +++ b/protocol/lib/collections_test.go @@ -67,6 +67,24 @@ func TestDedupeSlice(t *testing.T) { } } +func BenchmarkContainsDuplicates_True(b *testing.B) { + var result bool + input := []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + for i := 0; i < b.N; i++ { + result = lib.ContainsDuplicates(input) + } + require.True(b, result) +} + +func BenchmarkContainsDuplicates_False(b *testing.B) { + var result bool + input := []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + for i := 0; i < b.N; i++ { + result = lib.ContainsDuplicates(input) + } + require.False(b, result) +} + func TestContainsDuplicates(t *testing.T) { tests := map[string]struct { input []uint32 @@ -80,11 +98,11 @@ func TestContainsDuplicates(t *testing.T) { input: []uint32{}, expected: false, }, - "True": { + "False": { input: []uint32{1, 2, 3, 4}, expected: false, }, - "False": { + "True": { input: []uint32{1, 2, 3, 4, 3}, expected: true, },