Docs and bench

This commit is contained in:
bloeys
2022-06-11 07:59:03 +04:00
parent 5a6e13b9f1
commit c55e5b0f01
5 changed files with 105 additions and 10 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -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) ![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)). 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 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. This is because you might get two numbers like `1` and `1_000_000` which NSet 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. 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` With 10M ordered elements NSet takes `~31ms` and map `~97ms`, but with a random 10M elements NSet takes `~525ms`
while map takes `~9ms`. Map scales with the amount of elements, while NSet is affected by number distribution as well. 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 ## How NSet works

View File

@ -447,6 +447,8 @@ func BenchmarkMapIsEq(b *testing.B) {
} }
} }
var getIntersectionNset *nset.NSet[uint32]
func BenchmarkNSetGetIntersection(b *testing.B) { func BenchmarkNSetGetIntersection(b *testing.B) {
b.StopTimer() b.StopTimer()
@ -459,7 +461,93 @@ func BenchmarkNSetGetIntersection(b *testing.B) {
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { 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() b.StopTimer()
s1 := nset.NewNSet[uint32]() s1 := nset.NewNSet[uint32]()
for i := uint32(0); i < 1000_000; i++ { for i := uint32(0); i < 10_000_000; i++ {
s1.Add(i) s1.Add(i)
} }
b.StartTimer() b.StartTimer()
@ -488,7 +576,7 @@ func BenchmarkMapGetAllElements(b *testing.B) {
b.StopTimer() b.StopTimer()
m1 := map[uint32]struct{}{} m1 := map[uint32]struct{}{}
for i := uint32(0); i < 1000_000; i++ { for i := uint32(0); i < 10_000_000; i++ {
m1[i] = struct{}{} m1[i] = struct{}{}
} }
b.StartTimer() b.StartTimer()
@ -517,7 +605,7 @@ func BenchmarkNSetGetAllElementsRand(b *testing.B) {
rand.Seed(RandSeed) rand.Seed(RandSeed)
s1 := nset.NewNSet[uint32]() 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()) s1.Add(rand.Uint32())
} }
b.StartTimer() b.StartTimer()
@ -537,7 +625,7 @@ func BenchmarkMapGetAllElementsRand(b *testing.B) {
rand.Seed(RandSeed) rand.Seed(RandSeed)
m1 := map[uint32]struct{}{} 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{}{} m1[rand.Uint32()] = struct{}{}
} }