diff --git a/.golangci.yml b/.golangci.yml index 22de391..616503e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,6 +13,7 @@ linters: disable: - nonamedreturns - testpackage + - structcheck - varnamelen - ireturn - gofumpt diff --git a/README.md b/README.md index e0375b0..2a3a48c 100644 --- a/README.md +++ b/README.md @@ -60,16 +60,21 @@ func main() { ``` # benchmark + ``` +goos: linux +goarch: amd64 pkg: github.com/s0rg/quadtree cpu: AMD Ryzen 5 5500U with Radeon Graphics -BenchmarkNodeInsert1-12 4119890 302.1 ns/op 139 B/op 0 allocs/op -BenchmarkNodeInsert10-12 314479 3384 ns/op 1237 B/op 0 allocs/op -BenchmarkNodeInsert100-12 33385 33683 ns/op 14128 B/op 0 allocs/op -BenchmarkNodeDel10-12 38428 49986 ns/op 0 B/op 0 allocs/op -BenchmarkNodeDel100-12 10000 647607 ns/op 0 B/op 0 allocs/op -BenchmarkNodeSearch10-12 375060 3071 ns/op 0 B/op 0 allocs/op -BenchmarkNodeSearch100-12 37975 31457 ns/op 0 B/op 0 allocs/op -BenchmarkTreeKNearest10-12 639724 1842 ns/op 0 B/op 0 allocs/op -BenchmarkTreeKNearest100-12 64370 18922 ns/op 0 B/op 0 allocs/op +BenchmarkNode/Insert-12 14974236 71.07 ns/op 249 B/op 0 allocs/op +BenchmarkNode/Del-12 6415672 188.3 ns/op 0 B/op 0 allocs/op +BenchmarkNode/Search-12 21702474 51.83 ns/op 0 B/op 0 allocs/op +BenchmarkTree/Add-12 18840514 67.83 ns/op 241 B/op 0 allocs/op +BenchmarkTree/Get-12 21204722 55.46 ns/op 0 B/op 0 allocs/op +BenchmarkTree/Move-12 8061322 147.5 ns/op 0 B/op 0 allocs/op +BenchmarkTree/ForEach-12 18723290 58.60 ns/op 0 B/op 0 allocs/op +BenchmarkTree/KNearest-12 3595956 324.7 ns/op 0 B/op 0 allocs/op +BenchmarkTree/Del-12 6234123 193.1 ns/op 0 B/op 0 allocs/op +PASS +ok github.com/s0rg/quadtree 12.666s ``` diff --git a/go.mod b/go.mod index 37069d5..bc5b436 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/s0rg/quadtree -go 1.19 +go 1.20 diff --git a/node_test.go b/node_test.go index cb6fd48..229fc55 100644 --- a/node_test.go +++ b/node_test.go @@ -130,107 +130,44 @@ func TestNodeDel(t *testing.T) { }) } -const benchmarkSide = 1000.0 - -var benchmarkSeed = time.Now().UnixNano() - -func BenchmarkNodeInsert1(b *testing.B) { benchmarkInsertN(b, 1) } -func BenchmarkNodeInsert10(b *testing.B) { benchmarkInsertN(b, 10) } -func BenchmarkNodeInsert100(b *testing.B) { benchmarkInsertN(b, 100) } - -func BenchmarkNodeDel10(b *testing.B) { benchmarkDelN(b, 10) } -func BenchmarkNodeDel100(b *testing.B) { benchmarkDelN(b, 100) } - -func BenchmarkNodeSearch10(b *testing.B) { benchmarkSearchN(b, 10) } -func BenchmarkNodeSearch100(b *testing.B) { benchmarkSearchN(b, 100) } - -func benchmarkInsertN(b *testing.B, count int) { - b.Helper() - - const maxSize = 10.0 - - rand.Seed(benchmarkSeed) +func BenchmarkNode(b *testing.B) { + const ( + benchmarkSide = 1000.0 + benchmarkSize = 100 + maxSize = 10.0 + ) node := makeNode[int](rc(0, 0, benchmarkSide, benchmarkSide)) node.Grow(nodeTestDepth, 0) - b.ResetTimer() - - for n := 0; n < b.N; n++ { - for i := 0; i < count; i++ { - b.StopTimer() - - rc := randRect(benchmarkSide-maxSize, maxSize) - - b.StartTimer() - - node.Insert(rc, i) - } - } -} - -func benchmarkDelN(b *testing.B, count int) { - b.Helper() - - const maxSize = 10.0 - - rand.Seed(benchmarkSeed) - - node := makeNode[int](rc(0, 0, benchmarkSide, benchmarkSide)) - node.Grow(nodeTestDepth, 0) + rc := randRect(benchmarkSide-maxSize, maxSize) + x, y := randPoint(benchmarkSide - maxSize) b.ResetTimer() - for n := 0; n < b.N; n++ { - b.StopTimer() - - for i := 0; i < count; i++ { - rc := randRect(benchmarkSide-maxSize, maxSize) - node.Insert(rc, i) + b.Run("Insert", func(b *testing.B) { + for n := 0; n < b.N; n++ { + node.Insert(rc, n) } + }) - for i := 0; i < count; i++ { - x, y := randPoint(benchmarkSide - maxSize) - - b.StartTimer() + b.Run("Del", func(b *testing.B) { + for n := 0; n < b.N; n++ { node.Del(x, y) - b.StopTimer() } - } -} - -func benchmarkSearchN(b *testing.B, count int) { - b.Helper() - - const maxSize = 10.0 - - rand.Seed(benchmarkSeed) - - node := makeNode[int](rc(0, 0, benchmarkSide, benchmarkSide)) - node.Grow(nodeTestDepth, 0) - - for i := 0; i < count; i++ { - rc := randRect(benchmarkSide-maxSize, maxSize) - node.Insert(rc, i) - } - - b.ResetTimer() - - for n := 0; n < b.N; n++ { - for i := 0; i < count; i++ { - b.StopTimer() - - rc := randRect(benchmarkSide-maxSize, maxSize) - - b.StartTimer() + }) + b.Run("Search", func(b *testing.B) { + for n := 0; n < b.N; n++ { node.Search(rc, func(_ *item[int]) bool { return false }) } - } + }) } +var rng = rand.New(rand.NewSource(time.Now().UnixNano())) + func randFloat(min, max float64) (rv float64) { - return min + (rand.Float64() * (max - min)) + return min + (rng.Float64() * (max - min)) } func randRect(maxPos, maxSide float64) (r rect) { diff --git a/tree_test.go b/tree_test.go index 0102d25..88f4c5d 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1,7 +1,6 @@ package quadtree import ( - "math/rand" "testing" ) @@ -141,36 +140,53 @@ func TestKNearest(t *testing.T) { } } -func BenchmarkTreeKNearest10(b *testing.B) { benchmarkKNearestN(b, 10) } -func BenchmarkTreeKNearest100(b *testing.B) { benchmarkKNearestN(b, 100) } - -func benchmarkKNearestN(b *testing.B, count int) { - b.Helper() - - const maxSize = 10.0 - - rand.Seed(benchmarkSeed) +func BenchmarkTree(b *testing.B) { + const ( + benchmarkSide = 1000.0 + maxSize = 10.0 + ) q := New[int](benchmarkSide, benchmarkSide, nodeTestDepth) - for i := 0; i < count; i++ { - rc := randRect(benchmarkSide-maxSize, maxSize) - q.Add(rc.X0, rc.Y0, rc.Width(), rc.Heigth(), i) - } + x, y := 1.0, 1.0 + x2, y2 := 800.0, 800.0 b.ResetTimer() - for n := 0; n < b.N; n++ { - for i := 0; i < count; i++ { - b.StopTimer() + b.Run("Add", func(b *testing.B) { + for n := 0; n < b.N; n++ { + q.Add(x, y, maxSize, maxSize, n) + } + }) - x, y := randPoint(maxSize) - dist := randFloat(1, maxSize) - k := int(randFloat(1, maxSize)) + b.Run("Get", func(b *testing.B) { + for n := 0; n < b.N; n++ { + _, _ = q.Get(x, y, maxSize, maxSize) + } + }) - b.StartTimer() + b.Run("Move", func(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = q.Move(x, y, x2, y2) + x, y, x2, y2 = x2, y2, x, y + } + }) - q.KNearest(x, y, dist, k, func(_, _, _, _ float64, val int) {}) + b.Run("ForEach", func(b *testing.B) { + for n := 0; n < b.N; n++ { + q.ForEach(x, y, maxSize, maxSize, func(_, _, _, _ float64, val int) {}) } - } + }) + + b.Run("KNearest", func(b *testing.B) { + for n := 0; n < b.N; n++ { + q.KNearest(x, y, maxSize, int(maxSize), func(_, _, _, _ float64, val int) {}) + } + }) + + b.Run("Del", func(b *testing.B) { + for n := 0; n < b.N; n++ { + _ = q.Del(x, y) + } + }) }