diff --git a/.res/bench-getAllElements-1-million.png b/.res/bench-getAllElements-1-million.png deleted file mode 100755 index d454ff6..0000000 Binary files a/.res/bench-getAllElements-1-million.png and /dev/null differ diff --git a/.res/bench-getAllElements-10-million.png b/.res/bench-getAllElements-10-million.png new file mode 100755 index 0000000..0c32adc Binary files /dev/null and b/.res/bench-getAllElements-10-million.png differ diff --git a/.res/bench-getIntersection-10-million.png b/.res/bench-getIntersection-10-million.png new file mode 100755 index 0000000..059c330 Binary files /dev/null and b/.res/bench-getIntersection-10-million.png differ diff --git a/README.md b/README.md index 8d7655c..7126f15 100644 --- a/README.md +++ b/README.md @@ -138,14 +138,21 @@ Here NSet finishes in `0.1ms` but Map takes almost a second with `813ms`. ![Benchmarking IsEq with 10,000,000 elements](./.res/bench-is-equal-10-million.png) Next we have `GetAllElements`, which simply returns an array of all the elements of NSet/Map (note this is dangerous in NSet. See [Memory characteristics](#memory-characteristics)). -![Benchmarking GetAllElements with 1,000,000 elements](.res/bench-getAllElements-1-million.png) +![Benchmarking GetAllElements with 10,000,000 elements](.res/bench-getAllElements-10-million.png) -With `GetAllElements` NSet is faster when its elements are closer together (or you have many numbers), but gets a lot slower when -dealing with a few random numbers. This is because you might get two numbers like `1` and `1_000_000` which NSet +With `GetAllElements` NSet is faster when its elements are closer together value wise (or if you have many numbers), but gets a lot slower when +dealing with a few random numbers with a big difference between them. This is because you might get two numbers like `1` and `1_000_000` which NSet will store in two far away places with a lot of nothing in between. In a map these will be stored close together. -With 1M ordered elements NSet takes `~2ms` and map `~9ms`, but with a random 1M elements NSet takes `~129ms` -while map takes `~9ms`. Map scales with the amount of elements, while NSet is affected by number distribution as well. +With 10M ordered elements NSet takes `~31ms` and map `~97ms`, but with a random 10M elements NSet takes `~525ms` +while map takes `~95ms`. Map scales with the amount of elements, while NSet is affected by number distribution as well. + +Similar to getting elements is intersection: + +![Benchmarking GetIntersection with 10,000,000 elements](.res/bench-getIntersection-10-million.png) + +Here NSet is always many times faster, but the effect of number distribution on NSet's performance is clear, while map's performance +only scales with number of elements. ## How NSet works diff --git a/nset_test.go b/nset_test.go index 7d5f6de..2c58c39 100755 --- a/nset_test.go +++ b/nset_test.go @@ -447,6 +447,8 @@ func BenchmarkMapIsEq(b *testing.B) { } } +var getIntersectionNset *nset.NSet[uint32] + func BenchmarkNSetGetIntersection(b *testing.B) { b.StopTimer() @@ -459,7 +461,93 @@ func BenchmarkNSetGetIntersection(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - s1.GetIntersection(s2) + getIntersectionNset = s1.GetIntersection(s2) + } +} + +var getIntersectionTempMap map[uint32]struct{} + +func BenchmarkMapGetIntersection(b *testing.B) { + + b.StopTimer() + m1 := map[uint32]struct{}{} + m2 := map[uint32]struct{}{} + for i := uint32(0); i < maxBenchSize; i++ { + m1[i] = struct{}{} + m2[i] = struct{}{} + } + b.StartTimer() + + getIntersection := func(m1, m2 map[uint32]struct{}) map[uint32]struct{} { + + outMap := map[uint32]struct{}{} + + for k := range m1 { + if _, ok := m2[k]; ok { + outMap[k] = struct{}{} + } + } + + return outMap + } + + for i := 0; i < b.N; i++ { + getIntersectionTempMap = getIntersection(m1, m2) + } +} + +func BenchmarkNSetGetIntersectionRand(b *testing.B) { + + b.StopTimer() + + rand.Seed(RandSeed) + + s1 := nset.NewNSet[uint32]() + s2 := nset.NewNSet[uint32]() + for i := uint32(0); i < maxBenchSize; i++ { + + r := rand.Uint32() + s1.Add(r) + s2.Add(r) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + getIntersectionNset = s1.GetIntersection(s2) + } +} + +func BenchmarkMapGetIntersectionRand(b *testing.B) { + + b.StopTimer() + + rand.Seed(RandSeed) + + m1 := map[uint32]struct{}{} + m2 := map[uint32]struct{}{} + for i := uint32(0); i < maxBenchSize; i++ { + + r := rand.Uint32() + m1[r] = struct{}{} + m2[r] = struct{}{} + } + b.StartTimer() + + getIntersection := func(m1, m2 map[uint32]struct{}) map[uint32]struct{} { + + outMap := map[uint32]struct{}{} + + for k := range m1 { + if _, ok := m2[k]; ok { + outMap[k] = struct{}{} + } + } + + return outMap + } + + for i := 0; i < b.N; i++ { + getIntersectionTempMap = getIntersection(m1, m2) } } @@ -470,7 +558,7 @@ func BenchmarkNSetGetAllElements(b *testing.B) { b.StopTimer() s1 := nset.NewNSet[uint32]() - for i := uint32(0); i < 1000_000; i++ { + for i := uint32(0); i < 10_000_000; i++ { s1.Add(i) } b.StartTimer() @@ -488,7 +576,7 @@ func BenchmarkMapGetAllElements(b *testing.B) { b.StopTimer() m1 := map[uint32]struct{}{} - for i := uint32(0); i < 1000_000; i++ { + for i := uint32(0); i < 10_000_000; i++ { m1[i] = struct{}{} } b.StartTimer() @@ -517,7 +605,7 @@ func BenchmarkNSetGetAllElementsRand(b *testing.B) { rand.Seed(RandSeed) s1 := nset.NewNSet[uint32]() - for i := uint32(0); i < 1000_000; i++ { + for i := uint32(0); i < 10_000_000; i++ { s1.Add(rand.Uint32()) } b.StartTimer() @@ -537,7 +625,7 @@ func BenchmarkMapGetAllElementsRand(b *testing.B) { rand.Seed(RandSeed) m1 := map[uint32]struct{}{} - for i := uint32(0); i < 1000_000; i++ { + for i := uint32(0); i < 10_000_000; i++ { m1[rand.Uint32()] = struct{}{} }