From cd53f7d17722e4ef03a0ae76d74a70a07df2ae25 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 17 Jan 2020 16:07:49 +0200 Subject: [PATCH] lib/uint64set: improve benchmark for Set.Intersect --- lib/uint64set/uint64set_test.go | 48 ++++++++++++----- lib/uint64set/uint64set_timing_test.go | 72 +++++++++++++++++++------- 2 files changed, 86 insertions(+), 34 deletions(-) diff --git a/lib/uint64set/uint64set_test.go b/lib/uint64set/uint64set_test.go index b53bce9e4..e8ac8ebd1 100644 --- a/lib/uint64set/uint64set_test.go +++ b/lib/uint64set/uint64set_test.go @@ -232,32 +232,52 @@ func testSetBasicOps(t *testing.T, itemsCount int) { if n := s3.Len(); n != expectedLen { t.Fatalf("unexpected s3.Len() after union with empty set; got %d; want %d", n, expectedLen) } + var s4 Set + expectedLen = s3.Len() + s3.Union(&s4) + if n := s3.Len(); n != expectedLen { + t.Fatalf("unexpected s3.Len() after union with empty set; got %d; want %d", n, expectedLen) + } } // Verify intersect { - const intersectOffset = 12345 + // Verify s1.Intersect(s2) and s2.Intersect(s1) var s1, s2 Set - for i := 0; i < itemsCount; i++ { - s1.Add(uint64(i) + offset) - s2.Add(uint64(i) + offset + intersectOffset) - } - s1.Intersect(&s2) - expectedLen := 0 - if itemsCount > intersectOffset { - expectedLen = itemsCount - intersectOffset - } - if n := s1.Len(); n != expectedLen { - t.Fatalf("unexpected s1.Len() after intersect; got %d; want %d", n, expectedLen) + for _, intersectOffset := range []uint64{123, 12345, 1<<32 + 4343} { + s1 = Set{} + s2 = Set{} + for i := 0; i < itemsCount; i++ { + s1.Add(uint64(i) + offset) + s2.Add(uint64(i) + offset + intersectOffset) + } + expectedLen := 0 + if uint64(itemsCount) > intersectOffset { + expectedLen = int(uint64(itemsCount) - intersectOffset) + } + s1Copy := s1.Clone() + s1Copy.Intersect(&s2) + if n := s1Copy.Len(); n != expectedLen { + t.Fatalf("unexpected s1.Len() after intersect; got %d; want %d", n, expectedLen) + } + s2.Intersect(&s1) + if n := s2.Len(); n != expectedLen { + t.Fatalf("unexpected s2.Len() after intersect; got %d; want %d", n, expectedLen) + } } // Verify intersect on empty set. var s3 Set s2.Intersect(&s3) - expectedLen = 0 - if n := s2.Len(); n != 0 { + expectedLen := 0 + if n := s2.Len(); n != expectedLen { t.Fatalf("unexpected s3.Len() after intersect with empty set; got %d; want %d", n, expectedLen) } + var s4 Set + s4.Intersect(&s1) + if n := s4.Len(); n != expectedLen { + t.Fatalf("unexpected s4.Len() after intersect with empty set; got %d; want %d", n, expectedLen) + } } // Verify subtract diff --git a/lib/uint64set/uint64set_timing_test.go b/lib/uint64set/uint64set_timing_test.go index 9a23e5da6..864b4d34f 100644 --- a/lib/uint64set/uint64set_timing_test.go +++ b/lib/uint64set/uint64set_timing_test.go @@ -8,31 +8,56 @@ import ( "github.com/valyala/fastrand" ) -func BenchmarkIntersect(b *testing.B) { - const itemsCount = 3e6 - for _, lastBits := range []uint64{20, 24, 28, 32} { - sa := createRandomSet(itemsCount, lastBits) - sb := createRandomSet(itemsCount, lastBits) - b.Run(fmt.Sprintf("lastBits_%d", lastBits), func(b *testing.B) { - b.ReportAllocs() - b.SetBytes(int64(sa.Len()+sb.Len())) - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - saCopy := sa.Clone() - saCopy.Intersect(sb) - } - }) +func BenchmarkIntersectNoOverlap(b *testing.B) { + for _, itemsCount := range []int{1e3, 1e4, 1e5, 1e6, 1e7} { + start := uint64(time.Now().UnixNano()) + sa := createRangeSet(start, itemsCount) + sb := createRangeSet(start+uint64(itemsCount), itemsCount) + b.Run(fmt.Sprintf("items_%d", itemsCount), func(b *testing.B) { + benchmarkIntersect(b, sa, sb) }) } } -func createRandomSet(itemsCount int, lastBits uint64) *Set { - mask := (uint64(1) << lastBits) - 1 - start := uint64(time.Now().UnixNano()) +func BenchmarkIntersectPartialOverlap(b *testing.B) { + for _, itemsCount := range []int{1e3, 1e4, 1e5, 1e6, 1e7} { + start := uint64(time.Now().UnixNano()) + sa := createRangeSet(start, itemsCount) + sb := createRangeSet(start+uint64(itemsCount/2), itemsCount) + b.Run(fmt.Sprintf("items_%d", itemsCount), func(b *testing.B) { + benchmarkIntersect(b, sa, sb) + }) + } +} + +func BenchmarkIntersectFullOverlap(b *testing.B) { + for _, itemsCount := range []int{1e3, 1e4, 1e5, 1e6, 1e7} { + start := uint64(time.Now().UnixNano()) + sa := createRangeSet(start, itemsCount) + sb := createRangeSet(start, itemsCount) + b.Run(fmt.Sprintf("items_%d", itemsCount), func(b *testing.B) { + benchmarkIntersect(b, sa, sb) + }) + } +} + +func benchmarkIntersect(b *testing.B, sa, sb *Set) { + b.ReportAllocs() + b.SetBytes(int64(sa.Len() + sb.Len())) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + saCopy := sa.Clone() + sbCopy := sb.Clone() + saCopy.Intersect(sb) + sbCopy.Intersect(sa) + } + }) +} + +func createRangeSet(start uint64, itemsCount int) *Set { var s Set - var rng fastrand.RNG for i := 0; i < itemsCount; i++ { - n := start | uint64(rng.Uint32())&mask + n := start + uint64(i) s.Add(n) } return &s @@ -172,8 +197,15 @@ func BenchmarkMapAddReuse(b *testing.B) { func BenchmarkSetHasHitRandomLastBits(b *testing.B) { const itemsCount = 1e5 for _, lastBits := range []uint64{20, 24, 28, 32} { + mask := (uint64(1) << lastBits) - 1 b.Run(fmt.Sprintf("lastBits_%d", lastBits), func(b *testing.B) { - s := createRandomSet(itemsCount, lastBits) + start := uint64(time.Now().UnixNano()) + var s Set + var rng fastrand.RNG + for i := 0; i < itemsCount; i++ { + n := start | (uint64(rng.Uint32()) & mask) + s.Add(n) + } a := s.AppendTo(nil) b.ResetTimer()