From 014cf9750cab76f605a709e37a83bf5ebdfa5ebf Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 16:51:52 -0400 Subject: [PATCH 01/14] BFS memory improvement Declare the visit closure before the loop. --- bfs.go | 25 ++++++++++++++----------- bfs_test.go | 17 +++++++++++++++++ testdata/bench_after/BFS | 7 +++++++ testdata/bench_before/BFS | 7 +++++++ testdata/bench_results/BFS | 8 ++++++++ 5 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 testdata/bench_after/BFS create mode 100644 testdata/bench_before/BFS create mode 100644 testdata/bench_results/BFS diff --git a/bfs.go b/bfs.go index 91fe3d0..c364d51 100644 --- a/bfs.go +++ b/bfs.go @@ -7,17 +7,20 @@ package graph func BFS(g Iterator, v int, do func(v, w int, c int64)) { visited := make([]bool, g.Order()) visited[v] = true - for queue := []int{v}; len(queue) > 0; { - v := queue[0] - queue = queue[1:] - g.Visit(v, func(w int, c int64) (skip bool) { - if visited[w] { - return - } - do(v, w, c) - visited[w] = true - queue = append(queue, w) + queue := []int{v} + + fn := func(w int, c int64) (skip bool) { + if visited[w] { return - }) + } + do(v, w, c) + visited[w] = true + queue = append(queue, w) + return + } + for len(queue) > 0 { + v = queue[0] + queue = queue[1:] + g.Visit(v, fn) } } diff --git a/bfs_test.go b/bfs_test.go index a7f5a45..c54c46f 100644 --- a/bfs_test.go +++ b/bfs_test.go @@ -1,6 +1,7 @@ package graph import ( + "math/rand" "strconv" "testing" ) @@ -26,3 +27,19 @@ func TestBFS(t *testing.T) { t.Errorf("BFS: %s", mess) } } + +func BenchmarkBFS(b *testing.B) { + n := 1000 + g := New(n) + for i := 0; i < 5*n; i++ { + g.AddBoth(rand.Intn(n), rand.Intn(n)) + } + + fn := func(v, w int, c int64) {} + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + BFS(g, 0, fn) + } +} diff --git a/testdata/bench_after/BFS b/testdata/bench_after/BFS new file mode 100644 index 0000000..4d9cdb8 --- /dev/null +++ b/testdata/bench_after/BFS @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkBFS-8 5128 206326 ns/op 23213 B/op 17 allocs/op +PASS +ok github.com/rschio/graph 1.089s diff --git a/testdata/bench_before/BFS b/testdata/bench_before/BFS new file mode 100644 index 0000000..469edd6 --- /dev/null +++ b/testdata/bench_before/BFS @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkBFS-8 3776 269224 ns/op 86849 B/op 1015 allocs/op +PASS +ok github.com/rschio/graph 1.057s diff --git a/testdata/bench_results/BFS b/testdata/bench_results/BFS new file mode 100644 index 0000000..357c0ca --- /dev/null +++ b/testdata/bench_results/BFS @@ -0,0 +1,8 @@ +name old time/op new time/op delta +BFS-8 269µs ± 0% 206µs ± 0% -23.36% + +name old alloc/op new alloc/op delta +BFS-8 86.8kB ± 0% 23.2kB ± 0% -73.27% + +name old allocs/op new allocs/op delta +BFS-8 1.01k ± 0% 0.02k ± 0% -98.33% From d7b427e28f27bf83e8aaa609dc2c3a8817d51c83 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 17:27:48 -0400 Subject: [PATCH 02/14] testdata --- testdata/bench_after/placeholder | 0 testdata/bench_before/placeholder | 0 testdata/bench_results/placeholder | 0 testdata/benchdiff.sh | 2 ++ 4 files changed, 2 insertions(+) create mode 100644 testdata/bench_after/placeholder create mode 100644 testdata/bench_before/placeholder create mode 100644 testdata/bench_results/placeholder create mode 100755 testdata/benchdiff.sh diff --git a/testdata/bench_after/placeholder b/testdata/bench_after/placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/bench_before/placeholder b/testdata/bench_before/placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/bench_results/placeholder b/testdata/bench_results/placeholder new file mode 100644 index 0000000..e69de29 diff --git a/testdata/benchdiff.sh b/testdata/benchdiff.sh new file mode 100755 index 0000000..4b9914f --- /dev/null +++ b/testdata/benchdiff.sh @@ -0,0 +1,2 @@ +#!/bin/sh +benchstat -delta-test none testdata/bench_before/${1} testdata/bench_after/${1} | tee testdata/bench_results/${1} From 4c90f5030d8c6e8ba59be0f3dc787328d16802f1 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 17:21:52 -0400 Subject: [PATCH 03/14] Bipartition memory improvement Declare the closure before the loop. --- bipart.go | 41 +++++++++++++++++------------- bipart_test.go | 16 ++++++++++++ testdata/bench_after/Bipartition | 7 +++++ testdata/bench_before/Bipartition | 7 +++++ testdata/bench_results/Bipartition | 8 ++++++ 5 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 testdata/bench_after/Bipartition create mode 100644 testdata/bench_before/Bipartition create mode 100644 testdata/bench_results/Bipartition diff --git a/bipart.go b/bipart.go index 566157e..d02697e 100644 --- a/bipart.go +++ b/bipart.go @@ -12,31 +12,36 @@ func Bipartition(g Iterator) (part []int, ok bool) { ) colors := make([]color, g.Order()) whiteCount := 0 + + var src int + var queue []int + do := func(w int, _ int64) (skip bool) { + v := src + switch { + case colors[w] != none: + if colors[v] == colors[w] { + skip = true + } + return + case colors[v] == white: + colors[w] = black + default: + colors[w] = white + whiteCount++ + } + queue = append(queue, w) + return + } for v := range colors { if colors[v] != none { continue } colors[v] = white whiteCount++ - for queue := []int{v}; len(queue) > 0; { - v := queue[0] + for queue = []int{v}; len(queue) > 0; { + src = queue[0] queue = queue[1:] - if g.Visit(v, func(w int, _ int64) (skip bool) { - switch { - case colors[w] != none: - if colors[v] == colors[w] { - skip = true - } - return - case colors[v] == white: - colors[w] = black - default: - colors[w] = white - whiteCount++ - } - queue = append(queue, w) - return - }) { + if g.Visit(src, do) { return []int{}, false } } diff --git a/bipart_test.go b/bipart_test.go index 2b2f644..eb22301 100644 --- a/bipart_test.go +++ b/bipart_test.go @@ -1,6 +1,7 @@ package graph import ( + "math/rand" "testing" ) @@ -93,3 +94,18 @@ func TestBipartition(t *testing.T) { t.Errorf("Bipartition: %s", mess) } } + +func BenchmarkBipartition(b *testing.B) { + n := 1000 + g := New(n) + for i := 0; i < 3*n; i++ { + g.AddBoth(rand.Intn(n), rand.Intn(n)) + } + h := Sort(g) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = Bipartition(h) + } +} diff --git a/testdata/bench_after/Bipartition b/testdata/bench_after/Bipartition new file mode 100644 index 0000000..72dd884 --- /dev/null +++ b/testdata/bench_after/Bipartition @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkBipartition-8 2384719 1398 ns/op 2792 B/op 14 allocs/op +PASS +ok github.com/rschio/graph 3.860s diff --git a/testdata/bench_before/Bipartition b/testdata/bench_before/Bipartition new file mode 100644 index 0000000..142510f --- /dev/null +++ b/testdata/bench_before/Bipartition @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkBipartition-8 2253801 1875 ns/op 3616 B/op 26 allocs/op +PASS +ok github.com/rschio/graph 4.708s diff --git a/testdata/bench_results/Bipartition b/testdata/bench_results/Bipartition new file mode 100644 index 0000000..f8a3cab --- /dev/null +++ b/testdata/bench_results/Bipartition @@ -0,0 +1,8 @@ +name old time/op new time/op delta +Bipartition-8 1.88µs ± 0% 1.40µs ± 0% -25.44% + +name old alloc/op new alloc/op delta +Bipartition-8 3.62kB ± 0% 2.79kB ± 0% -22.79% + +name old allocs/op new allocs/op delta +Bipartition-8 26.0 ± 0% 14.0 ± 0% -46.15% From 35548d3769a4e4b764c2315aafb4f4a7bdaac5d6 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 20:56:50 -0400 Subject: [PATCH 04/14] Sort and Transpose memory improvement --- immutable.go | 27 +++++++++++++++------------ immutable_test.go | 26 ++++++++++++++++++++++++++ testdata/bench_after/Sort | 7 +++++++ testdata/bench_after/Transpose | 7 +++++++ testdata/bench_before/Sort | 7 +++++++ testdata/bench_before/Transpose | 7 +++++++ testdata/bench_results/Sort | 8 ++++++++ testdata/bench_results/Transpose | 8 ++++++++ 8 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 testdata/bench_after/Sort create mode 100644 testdata/bench_after/Transpose create mode 100644 testdata/bench_before/Sort create mode 100644 testdata/bench_before/Transpose create mode 100644 testdata/bench_results/Sort create mode 100644 testdata/bench_results/Transpose diff --git a/immutable.go b/immutable.go index 8f66979..e547e76 100644 --- a/immutable.go +++ b/immutable.go @@ -41,18 +41,21 @@ func Transpose(g Iterator) *Immutable { func build(g Iterator, transpose bool) *Immutable { n := g.Order() h := &Immutable{edges: make([][]neighbor, n)} - for v := range h.edges { - g.Visit(v, func(w int, c int64) (skip bool) { - if w < 0 || w >= n { - panic("vertex out of range: " + strconv.Itoa(w)) - } - if transpose { - h.edges[w] = append(h.edges[w], neighbor{v, c}) - } else { - h.edges[v] = append(h.edges[v], neighbor{w, c}) - } - return - }) + + var v int + do := func(w int, c int64) (skip bool) { + if w < 0 || w >= n { + panic("vertex out of range: " + strconv.Itoa(w)) + } + if transpose { + h.edges[w] = append(h.edges[w], neighbor{v, c}) + } else { + h.edges[v] = append(h.edges[v], neighbor{w, c}) + } + return + } + for v = range h.edges { + g.Visit(v, do) sort.Slice(h.edges[v], func(i, j int) bool { if e := h.edges[v]; e[i].vertex == e[j].vertex { return e[i].cost < e[j].cost diff --git a/immutable_test.go b/immutable_test.go index f530ab7..e2e9b5b 100644 --- a/immutable_test.go +++ b/immutable_test.go @@ -197,3 +197,29 @@ func TestDegreeImm(t *testing.T) { t.Errorf("g5c.Degree(1) %s", mess) } } + +func BenchmarkSort(b *testing.B) { + n := 1000 + g := New(n) + for i := 0; i < 3*n; i++ { + g.Add(rand.Intn(n), rand.Intn(n)) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = Sort(g) + } +} + +func BenchmarkTranspose(b *testing.B) { + n := 1000 + g := New(n) + for i := 0; i < 3*n; i++ { + g.Add(rand.Intn(n), rand.Intn(n)) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = Transpose(g) + } +} diff --git a/testdata/bench_after/Sort b/testdata/bench_after/Sort new file mode 100644 index 0000000..fb298dd --- /dev/null +++ b/testdata/bench_after/Sort @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkSort-8 2433 499979 ns/op 201219 B/op 5031 allocs/op +PASS +ok github.com/rschio/graph 1.272s diff --git a/testdata/bench_after/Transpose b/testdata/bench_after/Transpose new file mode 100644 index 0000000..3b2db5e --- /dev/null +++ b/testdata/bench_after/Transpose @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkTranspose-8 3045 401361 ns/op 169624 B/op 4002 allocs/op +PASS +ok github.com/rschio/graph 1.267s diff --git a/testdata/bench_before/Sort b/testdata/bench_before/Sort new file mode 100644 index 0000000..117ff1f --- /dev/null +++ b/testdata/bench_before/Sort @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkSort-8 2175 608159 ns/op 249174 B/op 6030 allocs/op +PASS +ok github.com/rschio/graph 1.383s diff --git a/testdata/bench_before/Transpose b/testdata/bench_before/Transpose new file mode 100644 index 0000000..95e07c0 --- /dev/null +++ b/testdata/bench_before/Transpose @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkTranspose-8 2698 404269 ns/op 217577 B/op 5001 allocs/op +PASS +ok github.com/rschio/graph 1.140s diff --git a/testdata/bench_results/Sort b/testdata/bench_results/Sort new file mode 100644 index 0000000..c71f8f9 --- /dev/null +++ b/testdata/bench_results/Sort @@ -0,0 +1,8 @@ +name old time/op new time/op delta +Sort-8 608µs ± 0% 500µs ± 0% -17.79% + +name old alloc/op new alloc/op delta +Sort-8 249kB ± 0% 201kB ± 0% -19.25% + +name old allocs/op new allocs/op delta +Sort-8 6.03k ± 0% 5.03k ± 0% -16.57% diff --git a/testdata/bench_results/Transpose b/testdata/bench_results/Transpose new file mode 100644 index 0000000..85583fc --- /dev/null +++ b/testdata/bench_results/Transpose @@ -0,0 +1,8 @@ +name old time/op new time/op delta +Transpose-8 404µs ± 0% 401µs ± 0% -0.72% + +name old alloc/op new alloc/op delta +Transpose-8 218kB ± 0% 170kB ± 0% -22.04% + +name old allocs/op new allocs/op delta +Transpose-8 5.00k ± 0% 4.00k ± 0% -19.98% From 7e7acaf9455b552caa64454577b44f586e161c72 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 19:51:18 -0400 Subject: [PATCH 05/14] MST memory improvement --- mst.go | 20 +++++++++++--------- mst_test.go | 1 + testdata/bench_after/MST | 7 +++++++ testdata/bench_before/MST | 7 +++++++ testdata/bench_results/MST | 8 ++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 testdata/bench_after/MST create mode 100644 testdata/bench_before/MST create mode 100644 testdata/bench_results/MST diff --git a/mst.go b/mst.go index dc72292..7a04f89 100644 --- a/mst.go +++ b/mst.go @@ -19,16 +19,18 @@ func MST(g Iterator) (parent []int) { // Prim's algorithm Q := newPrioQueue(cost) + var v int + do := func(w int, c int64) (skip bool) { + if Q.Contains(w) && c < cost[w] { + cost[w] = c + Q.Fix(w) + parent[w] = v + } + return + } for Q.Len() > 0 { - v := Q.Pop() - g.Visit(v, func(w int, c int64) (skip bool) { - if Q.Contains(w) && c < cost[w] { - cost[w] = c - Q.Fix(w) - parent[w] = v - } - return - }) + v = Q.Pop() + g.Visit(v, do) } return } diff --git a/mst_test.go b/mst_test.go index dff0c47..092b461 100644 --- a/mst_test.go +++ b/mst_test.go @@ -34,6 +34,7 @@ func TestMST(t *testing.T) { func BenchmarkMST(b *testing.B) { n := 1000 + b.ReportAllocs() b.StopTimer() g := New(n) for i := 0; i < 2*n; i++ { diff --git a/testdata/bench_after/MST b/testdata/bench_after/MST new file mode 100644 index 0000000..52f14dc --- /dev/null +++ b/testdata/bench_after/MST @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkMST-8 5379 189717 ns/op 32951 B/op 8 allocs/op +PASS +ok github.com/rschio/graph 1.048s diff --git a/testdata/bench_before/MST b/testdata/bench_before/MST new file mode 100644 index 0000000..f261fba --- /dev/null +++ b/testdata/bench_before/MST @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkMST-8 4191 249128 ns/op 96883 B/op 1006 allocs/op +PASS +ok github.com/rschio/graph 1.078s diff --git a/testdata/bench_results/MST b/testdata/bench_results/MST new file mode 100644 index 0000000..d3c706b --- /dev/null +++ b/testdata/bench_results/MST @@ -0,0 +1,8 @@ +name old time/op new time/op delta +MST-8 249µs ± 0% 190µs ± 0% -23.85% + +name old alloc/op new alloc/op delta +MST-8 96.9kB ± 0% 33.0kB ± 0% -65.99% + +name old allocs/op new allocs/op delta +MST-8 1.01k ± 0% 0.01k ± 0% -99.20% From 221ba125030e557d5b2a9253d528a5a94a2215e8 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 20:32:28 -0400 Subject: [PATCH 06/14] tiny memory improvement --- maxflow.go | 40 +++++++++++++++++++--------------- maxflow_test.go | 3 ++- testdata/bench_after/MaxFlow | 7 ++++++ testdata/bench_before/MaxFlow | 7 ++++++ testdata/bench_results/MaxFlow | 8 +++++++ 5 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 testdata/bench_after/MaxFlow create mode 100644 testdata/bench_before/MaxFlow create mode 100644 testdata/bench_results/MaxFlow diff --git a/maxflow.go b/maxflow.go index 641db44..eb0d742 100644 --- a/maxflow.go +++ b/maxflow.go @@ -27,13 +27,15 @@ func MaxFlow(g Iterator, s, t int) (flow int64, graph Iterator) { } } res := New(n) - for v := 0; v < n; v++ { - g.Visit(v, func(w int, c int64) (skip bool) { - if flow := c - residual.Cost(v, w); flow > 0 { - res.AddCost(v, w, flow) - } - return - }) + var v int + do := func(w int, c int64) (skip bool) { + if flow := c - residual.Cost(v, w); flow > 0 { + res.AddCost(v, w, flow) + } + return + } + for v = 0; v < n; v++ { + g.Visit(v, do) } return flow, Sort(res) } @@ -41,17 +43,21 @@ func MaxFlow(g Iterator, s, t int) (flow int64, graph Iterator) { func residualFlow(g *Mutable, s, t int, prev []int) bool { visited := make([]bool, g.Order()) prev[s], visited[s] = -1, true - for queue := []int{s}; len(queue) > 0; { - v := queue[0] + queue := []int{s} + + var v int + do := func(w int, c int64) (skip bool) { + if !visited[w] && c > 0 { + prev[w] = v + visited[w] = true + queue = append(queue, w) + } + return + } + for len(queue) > 0 { + v = queue[0] queue = queue[1:] - g.Visit(v, func(w int, c int64) (skip bool) { - if !visited[w] && c > 0 { - prev[w] = v - visited[w] = true - queue = append(queue, w) - } - return - }) + g.Visit(v, do) } return visited[t] } diff --git a/maxflow_test.go b/maxflow_test.go index 350d8a5..a018919 100644 --- a/maxflow_test.go +++ b/maxflow_test.go @@ -70,7 +70,8 @@ func TestMaxFlow(t *testing.T) { } func BenchmarkMaxFlow(b *testing.B) { - n := 50 + n := 500 + b.ReportAllocs() b.StopTimer() g := New(n) for i := 0; i < n; i++ { diff --git a/testdata/bench_after/MaxFlow b/testdata/bench_after/MaxFlow new file mode 100644 index 0000000..5adc2ae --- /dev/null +++ b/testdata/bench_after/MaxFlow @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkMaxFlow-8 1 2062536970 ns/op 15380024 B/op 21500 allocs/op +PASS +ok github.com/rschio/graph 2.087s diff --git a/testdata/bench_before/MaxFlow b/testdata/bench_before/MaxFlow new file mode 100644 index 0000000..2a674e0 --- /dev/null +++ b/testdata/bench_before/MaxFlow @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkMaxFlow-8 1 2052845414 ns/op 15570528 B/op 22207 allocs/op +PASS +ok github.com/rschio/graph 2.073s diff --git a/testdata/bench_results/MaxFlow b/testdata/bench_results/MaxFlow new file mode 100644 index 0000000..ffbb379 --- /dev/null +++ b/testdata/bench_results/MaxFlow @@ -0,0 +1,8 @@ +name old time/op new time/op delta +MaxFlow-8 2.05s ± 0% 2.06s ± 0% +0.47% + +name old alloc/op new alloc/op delta +MaxFlow-8 15.6MB ± 0% 15.4MB ± 0% -1.22% + +name old allocs/op new allocs/op delta +MaxFlow-8 22.2k ± 0% 21.5k ± 0% -3.18% From 9b14b1a88dd1256cc9f4af44ad0e8cf05281085a Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 21:21:25 -0400 Subject: [PATCH 07/14] MaxFlow bench after Sort -- TODO should update MaxFlow? --- testdata/bench_after/MaxFlow | 4 ++-- testdata/bench_results/MaxFlow | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/testdata/bench_after/MaxFlow b/testdata/bench_after/MaxFlow index 5adc2ae..0a87a50 100644 --- a/testdata/bench_after/MaxFlow +++ b/testdata/bench_after/MaxFlow @@ -2,6 +2,6 @@ goos: linux goarch: amd64 pkg: github.com/rschio/graph cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz -BenchmarkMaxFlow-8 1 2062536970 ns/op 15380024 B/op 21500 allocs/op +BenchmarkMaxFlow-8 1 2086161963 ns/op 15496176 B/op 21334 allocs/op PASS -ok github.com/rschio/graph 2.087s +ok github.com/rschio/graph 2.108s diff --git a/testdata/bench_results/MaxFlow b/testdata/bench_results/MaxFlow index ffbb379..7c3a027 100644 --- a/testdata/bench_results/MaxFlow +++ b/testdata/bench_results/MaxFlow @@ -1,8 +1,8 @@ name old time/op new time/op delta -MaxFlow-8 2.05s ± 0% 2.06s ± 0% +0.47% +MaxFlow-8 2.05s ± 0% 2.09s ± 0% +1.62% name old alloc/op new alloc/op delta -MaxFlow-8 15.6MB ± 0% 15.4MB ± 0% -1.22% +MaxFlow-8 15.6MB ± 0% 15.5MB ± 0% -0.48% name old allocs/op new allocs/op delta -MaxFlow-8 22.2k ± 0% 21.5k ± 0% -3.18% +MaxFlow-8 22.2k ± 0% 21.3k ± 0% -3.93% From 7ab9e02f9b5944f269fcf356a1776c4332fb0df7 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 16:31:48 -0400 Subject: [PATCH 08/14] ShortestPath(s) memory improvement removes the visit closure from inside the loop. ShortestPath now stops when the target vertex is found. --- path.go | 43 +++++++++++++++++----------- path_test.go | 16 +++++++++++ testdata/bench_after/ShortestPath | 7 +++++ testdata/bench_after/ShortestPaths | 7 +++++ testdata/bench_before/ShortestPath | 7 +++++ testdata/bench_before/ShortestPaths | 7 +++++ testdata/bench_results/ShortestPath | 8 ++++++ testdata/bench_results/ShortestPaths | 8 ++++++ 8 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 testdata/bench_after/ShortestPath create mode 100644 testdata/bench_after/ShortestPaths create mode 100644 testdata/bench_before/ShortestPath create mode 100644 testdata/bench_before/ShortestPaths create mode 100644 testdata/bench_results/ShortestPath create mode 100644 testdata/bench_results/ShortestPaths diff --git a/path.go b/path.go index 850ddec..df86072 100644 --- a/path.go +++ b/path.go @@ -7,7 +7,7 @@ package graph // The time complexity is O((|E| + |V|)⋅log|V|), where |E| is the number of edges // and |V| the number of vertices in the graph. func ShortestPath(g Iterator, v, w int) (path []int, dist int64) { - parent, distances := ShortestPaths(g, v) + parent, distances := shortestPath(g, v, w) path, dist = []int{}, distances[w] if dist == -1 { return @@ -31,6 +31,12 @@ func ShortestPath(g Iterator, v, w int) (path []int, dist int64) { // The time complexity is O((|E| + |V|)⋅log|V|), where |E| is the number of edges // and |V| the number of vertices in the graph. func ShortestPaths(g Iterator, v int) (parent []int, dist []int64) { + // Use -1 to search for a vertex that doesn't exists so it will + // search for all the shortest paths from v. + return shortestPath(g, v, -1) +} + +func shortestPath(g Iterator, v, w int) (parent []int, dist []int64) { n := g.Order() dist = make([]int64, n) parent = make([]int, n) @@ -42,23 +48,28 @@ func ShortestPaths(g Iterator, v int) (parent []int, dist []int64) { // Dijkstra's algorithm Q := emptyPrioQueue(dist) Q.Push(v) + + do := func(w int, d int64) (skip bool) { + if d < 0 { + return + } + alt := dist[v] + d + switch { + case dist[w] == -1: + dist[w], parent[w] = alt, v + Q.Push(w) + case alt < dist[w]: + dist[w], parent[w] = alt, v + Q.Fix(w) + } + return + } for Q.Len() > 0 { - v := Q.Pop() - g.Visit(v, func(w int, d int64) (skip bool) { - if d < 0 { - return - } - alt := dist[v] + d - switch { - case dist[w] == -1: - dist[w], parent[w] = alt, v - Q.Push(w) - case alt < dist[w]: - dist[w], parent[w] = alt, v - Q.Fix(w) - } + v = Q.Pop() + if v == w { return - }) + } + g.Visit(v, do) } return } diff --git a/path_test.go b/path_test.go index 2350c8c..a3642f9 100644 --- a/path_test.go +++ b/path_test.go @@ -50,6 +50,21 @@ func TestShortestPath(t *testing.T) { } } +func BenchmarkShortestPath(b *testing.B) { + n := 1000 + b.StopTimer() + g := New(n) + for i := 0; i < n; i++ { + g.Add(0, rand.Intn(n)) + g.Add(rand.Intn(n), rand.Intn(n)) + } + b.ReportAllocs() + b.StartTimer() + for i := 0; i < b.N; i++ { + _, _ = ShortestPath(g, 0, n-1) + } +} + func BenchmarkShortestPaths(b *testing.B) { n := 1000 b.StopTimer() @@ -58,6 +73,7 @@ func BenchmarkShortestPaths(b *testing.B) { g.Add(0, rand.Intn(n)) g.Add(rand.Intn(n), rand.Intn(n)) } + b.ReportAllocs() b.StartTimer() for i := 0; i < b.N; i++ { _, _ = ShortestPaths(g, 0) diff --git a/testdata/bench_after/ShortestPath b/testdata/bench_after/ShortestPath new file mode 100644 index 0000000..63b0cc1 --- /dev/null +++ b/testdata/bench_after/ShortestPath @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkShortestPath-8 26695 60170 ns/op 39753 B/op 21 allocs/op +PASS +ok github.com/rschio/graph 3.108s diff --git a/testdata/bench_after/ShortestPaths b/testdata/bench_after/ShortestPaths new file mode 100644 index 0000000..8c6f9c9 --- /dev/null +++ b/testdata/bench_after/ShortestPaths @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkShortestPaths-8 14500 79967 ns/op 39729 B/op 19 allocs/op +PASS +ok github.com/rschio/graph 2.003s diff --git a/testdata/bench_before/ShortestPath b/testdata/bench_before/ShortestPath new file mode 100644 index 0000000..f63c789 --- /dev/null +++ b/testdata/bench_before/ShortestPath @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkShortestPath-8 8378 140683 ns/op 79875 B/op 856 allocs/op +PASS +ok github.com/rschio/graph 1.198s diff --git a/testdata/bench_before/ShortestPaths b/testdata/bench_before/ShortestPaths new file mode 100644 index 0000000..b578115 --- /dev/null +++ b/testdata/bench_before/ShortestPaths @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkShortestPaths-8 10394 121272 ns/op 79226 B/op 841 allocs/op +PASS +ok github.com/rschio/graph 2.118s diff --git a/testdata/bench_results/ShortestPath b/testdata/bench_results/ShortestPath new file mode 100644 index 0000000..94d9b58 --- /dev/null +++ b/testdata/bench_results/ShortestPath @@ -0,0 +1,8 @@ +name old time/op new time/op delta +ShortestPath-8 141µs ± 0% 60µs ± 0% -57.23% + +name old alloc/op new alloc/op delta +ShortestPath-8 79.9kB ± 0% 39.8kB ± 0% -50.23% + +name old allocs/op new allocs/op delta +ShortestPath-8 856 ± 0% 21 ± 0% -97.55% diff --git a/testdata/bench_results/ShortestPaths b/testdata/bench_results/ShortestPaths new file mode 100644 index 0000000..9407000 --- /dev/null +++ b/testdata/bench_results/ShortestPaths @@ -0,0 +1,8 @@ +name old time/op new time/op delta +ShortestPaths-8 121µs ± 0% 80µs ± 0% -34.06% + +name old alloc/op new alloc/op delta +ShortestPaths-8 79.2kB ± 0% 39.7kB ± 0% -49.85% + +name old allocs/op new allocs/op delta +ShortestPaths-8 841 ± 0% 19 ± 0% -97.74% From 0d5642eb54bec6ba406c1eab59a8dc5a5b3a0042 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 20:03:30 -0400 Subject: [PATCH 09/14] TopSort and Acyclic memory improvement --- testdata/bench_after/Acyclic | 7 +++++++ testdata/bench_after/TopSort | 7 +++++++ testdata/bench_before/Acyclic | 7 +++++++ testdata/bench_before/TopSort | 7 +++++++ testdata/bench_results/Acyclic | 8 ++++++++ testdata/bench_results/TopSort | 8 ++++++++ top.go | 24 +++++++++++++----------- top_test.go | 2 ++ 8 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 testdata/bench_after/Acyclic create mode 100644 testdata/bench_after/TopSort create mode 100644 testdata/bench_before/Acyclic create mode 100644 testdata/bench_before/TopSort create mode 100644 testdata/bench_results/Acyclic create mode 100644 testdata/bench_results/TopSort diff --git a/testdata/bench_after/Acyclic b/testdata/bench_after/Acyclic new file mode 100644 index 0000000..4681931 --- /dev/null +++ b/testdata/bench_after/Acyclic @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkAcyclic-8 16905 66498 ns/op 12386 B/op 13 allocs/op +PASS +ok github.com/rschio/graph 1.848s diff --git a/testdata/bench_after/TopSort b/testdata/bench_after/TopSort new file mode 100644 index 0000000..5edc163 --- /dev/null +++ b/testdata/bench_after/TopSort @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkTopSort-8 17028 69394 ns/op 16474 B/op 22 allocs/op +PASS +ok github.com/rschio/graph 1.900s diff --git a/testdata/bench_before/Acyclic b/testdata/bench_before/Acyclic new file mode 100644 index 0000000..27afc9b --- /dev/null +++ b/testdata/bench_before/Acyclic @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkAcyclic-8 9379 127766 ns/op 51988 B/op 1171 allocs/op +PASS +ok github.com/rschio/graph 2.072s diff --git a/testdata/bench_before/TopSort b/testdata/bench_before/TopSort new file mode 100644 index 0000000..2126721 --- /dev/null +++ b/testdata/bench_before/TopSort @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkTopSort-8 9980 134713 ns/op 56076 B/op 1180 allocs/op +PASS +ok github.com/rschio/graph 2.332s diff --git a/testdata/bench_results/Acyclic b/testdata/bench_results/Acyclic new file mode 100644 index 0000000..f057162 --- /dev/null +++ b/testdata/bench_results/Acyclic @@ -0,0 +1,8 @@ +name old time/op new time/op delta +Acyclic-8 128µs ± 0% 66µs ± 0% -47.95% + +name old alloc/op new alloc/op delta +Acyclic-8 52.0kB ± 0% 12.4kB ± 0% -76.18% + +name old allocs/op new allocs/op delta +Acyclic-8 1.17k ± 0% 0.01k ± 0% -98.89% diff --git a/testdata/bench_results/TopSort b/testdata/bench_results/TopSort new file mode 100644 index 0000000..0f97727 --- /dev/null +++ b/testdata/bench_results/TopSort @@ -0,0 +1,8 @@ +name old time/op new time/op delta +TopSort-8 135µs ± 0% 69µs ± 0% -48.49% + +name old alloc/op new alloc/op delta +TopSort-8 56.1kB ± 0% 16.5kB ± 0% -70.62% + +name old allocs/op new allocs/op delta +TopSort-8 1.18k ± 0% 0.02k ± 0% -98.14% diff --git a/top.go b/top.go index e03d97f..dc85611 100644 --- a/top.go +++ b/top.go @@ -19,11 +19,12 @@ func Acyclic(g Iterator) bool { // Kahn's algorithm func topsort(g Iterator, output bool) (order []int, acyclic bool) { indegree := make([]int, g.Order()) + addIndegree := func(w int, _ int64) (skip bool) { + indegree[w]++ + return + } for v := range indegree { - g.Visit(v, func(w int, _ int64) (skip bool) { - indegree[w]++ - return - }) + g.Visit(v, addIndegree) } // Invariant: this queue holds all vertices with indegree 0. @@ -34,6 +35,13 @@ func topsort(g Iterator, output bool) (order []int, acyclic bool) { } } + subIndegree := func(w int, _ int64) (skip bool) { + indegree[w]-- + if indegree[w] == 0 { + queue = append(queue, w) + } + return + } order = []int{} vertexCount := 0 for len(queue) > 0 { @@ -43,13 +51,7 @@ func topsort(g Iterator, output bool) (order []int, acyclic bool) { order = append(order, v) } vertexCount++ - g.Visit(v, func(w int, _ int64) (skip bool) { - indegree[w]-- - if indegree[w] == 0 { - queue = append(queue, w) - } - return - }) + g.Visit(v, subIndegree) } if vertexCount != g.Order() { return diff --git a/top_test.go b/top_test.go index 319c0ee..c042165 100644 --- a/top_test.go +++ b/top_test.go @@ -84,6 +84,7 @@ func TestAcyclic(t *testing.T) { func BenchmarkAcyclic(b *testing.B) { n := 1000 + b.ReportAllocs() b.StopTimer() g := New(n) for i := 0; i < 2*n; i++ { @@ -100,6 +101,7 @@ func BenchmarkAcyclic(b *testing.B) { func BenchmarkTopSort(b *testing.B) { n := 1000 + b.ReportAllocs() b.StopTimer() g := New(n) for i := 0; i < 2*n; i++ { From 452da85eec82189961260cf49e48c7e134cf7bcd Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 20:11:47 -0400 Subject: [PATCH 10/14] Connected and Components memory improvement --- testdata/bench_after/Components | 7 +++++++ testdata/bench_after/Connected | 7 +++++++ testdata/bench_before/Components | 7 +++++++ testdata/bench_before/Connected | 7 +++++++ testdata/bench_results/Components | 8 ++++++++ testdata/bench_results/Connected | 8 ++++++++ weak.go | 25 ++++++++++++++----------- weak_test.go | 2 ++ 8 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 testdata/bench_after/Components create mode 100644 testdata/bench_after/Connected create mode 100644 testdata/bench_before/Components create mode 100644 testdata/bench_before/Connected create mode 100644 testdata/bench_results/Components create mode 100644 testdata/bench_results/Connected diff --git a/testdata/bench_after/Components b/testdata/bench_after/Components new file mode 100644 index 0000000..41b1268 --- /dev/null +++ b/testdata/bench_after/Components @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkComponents-8 7056 148831 ns/op 54846 B/op 235 allocs/op +PASS +ok github.com/rschio/graph 1.072s diff --git a/testdata/bench_after/Connected b/testdata/bench_after/Connected new file mode 100644 index 0000000..b247c3c --- /dev/null +++ b/testdata/bench_after/Connected @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkConnected-8 11476 103912 ns/op 8267 B/op 5 allocs/op +PASS +ok github.com/rschio/graph 1.986s diff --git a/testdata/bench_before/Components b/testdata/bench_before/Components new file mode 100644 index 0000000..15719cf --- /dev/null +++ b/testdata/bench_before/Components @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkComponents-8 5728 195865 ns/op 86816 B/op 1234 allocs/op +PASS +ok github.com/rschio/graph 1.148s diff --git a/testdata/bench_before/Connected b/testdata/bench_before/Connected new file mode 100644 index 0000000..89109a9 --- /dev/null +++ b/testdata/bench_before/Connected @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkConnected-8 7894 151161 ns/op 40237 B/op 1004 allocs/op +PASS +ok github.com/rschio/graph 2.047s diff --git a/testdata/bench_results/Components b/testdata/bench_results/Components new file mode 100644 index 0000000..f9d7a86 --- /dev/null +++ b/testdata/bench_results/Components @@ -0,0 +1,8 @@ +name old time/op new time/op delta +Components-8 196µs ± 0% 149µs ± 0% -24.01% + +name old alloc/op new alloc/op delta +Components-8 86.8kB ± 0% 54.8kB ± 0% -36.83% + +name old allocs/op new allocs/op delta +Components-8 1.23k ± 0% 0.23k ± 0% -80.96% diff --git a/testdata/bench_results/Connected b/testdata/bench_results/Connected new file mode 100644 index 0000000..adc21e3 --- /dev/null +++ b/testdata/bench_results/Connected @@ -0,0 +1,8 @@ +name old time/op new time/op delta +Connected-8 151µs ± 0% 104µs ± 0% -31.26% + +name old alloc/op new alloc/op delta +Connected-8 40.2kB ± 0% 8.3kB ± 0% -79.45% + +name old allocs/op new allocs/op delta +Connected-8 1.00k ± 0% 0.01k ± 0% -99.50% diff --git a/weak.go b/weak.go index c07f033..43ffeb1 100644 --- a/weak.go +++ b/weak.go @@ -29,18 +29,21 @@ func Components(g Iterator) [][]int { func components(g Iterator) (sets disjointSets, count int) { n := g.Order() sets, count = makeSingletons(n), n - for v := 0; v < n && count > 1; v++ { - g.Visit(v, func(w int, _ int64) (skip bool) { - x, y := sets.find(v), sets.find(w) - if x != y { - sets.union(x, y) - count-- - if count == 1 { - skip = true - } + + var v int + do := func(w int, _ int64) (skip bool) { + x, y := sets.find(v), sets.find(w) + if x != y { + sets.union(x, y) + count-- + if count == 1 { + skip = true } - return - }) + } + return + } + for v = 0; v < n && count > 1; v++ { + g.Visit(v, do) } return } diff --git a/weak_test.go b/weak_test.go index 581a834..95fb78b 100644 --- a/weak_test.go +++ b/weak_test.go @@ -54,6 +54,7 @@ func TestComponents(t *testing.T) { func BenchmarkConnected(b *testing.B) { n := 1000 + b.ReportAllocs() b.StopTimer() g := New(n) for i := 0; i < n; i++ { @@ -67,6 +68,7 @@ func BenchmarkConnected(b *testing.B) { func BenchmarkComponents(b *testing.B) { n := 1000 + b.ReportAllocs() b.StopTimer() g := New(n) for i := 0; i < n; i++ { From ef18f9d337bdb73e31446dd04d88978bf9ae09ca Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 22:24:55 -0400 Subject: [PATCH 11/14] EulerDirected and EulerUndirected memory improvement --- euler.go | 47 ++++++++++++++++---------- euler_test.go | 37 ++++++++++++++++++++ testdata/bench_after/EulerDirected | 7 ++++ testdata/bench_after/EulerUndirected | 7 ++++ testdata/bench_before/EulerDirected | 7 ++++ testdata/bench_before/EulerUndirected | 7 ++++ testdata/bench_results/EulerDirected | 8 +++++ testdata/bench_results/EulerUndirected | 8 +++++ 8 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 testdata/bench_after/EulerDirected create mode 100644 testdata/bench_after/EulerUndirected create mode 100644 testdata/bench_before/EulerDirected create mode 100644 testdata/bench_before/EulerUndirected create mode 100644 testdata/bench_results/EulerDirected create mode 100644 testdata/bench_results/EulerUndirected diff --git a/euler.go b/euler.go index 52f959a..3fb8fc6 100644 --- a/euler.go +++ b/euler.go @@ -6,13 +6,18 @@ func EulerDirected(g Iterator) (walk []int, ok bool) { n := g.Order() degree := make([]int, n) // outdegree - indegree for each vertex edgeCount := 0 + + var src int + degreeFn := func(w int, _ int64) (skip bool) { + v := src + edgeCount++ + degree[v]++ + degree[w]-- + return + } for v := range degree { - g.Visit(v, func(w int, _ int64) (skip bool) { - edgeCount++ - degree[v]++ - degree[w]-- - return - }) + src = v + g.Visit(src, degreeFn) } if edgeCount == 0 { return []int{}, true @@ -33,11 +38,14 @@ func EulerDirected(g Iterator) (walk []int, ok bool) { // Make a copy of g h := make([][]int, n) + copyFn := func(w int, _ int64) (skip bool) { + v := src + h[v] = append(h[v], w) + return + } for v := range h { - g.Visit(v, func(w int, _ int64) (skip bool) { - h[v] = append(h[v], w) - return - }) + src = v + g.Visit(src, copyFn) } // Find a starting point with neighbors. @@ -77,14 +85,19 @@ func EulerUndirected(g Iterator) (walk []int, ok bool) { n := g.Order() out := make([]int, n) // outdegree for each vertex edgeCount := 0 + + var src int + outDegreeFn := func(w int, _ int64) (skip bool) { + v := src + edgeCount++ + if v != w { + out[v]++ + } + return + } for v := range out { - g.Visit(v, func(w int, _ int64) (skip bool) { - edgeCount++ - if v != w { - out[v]++ - } - return - }) + src = v + g.Visit(src, outDegreeFn) } if edgeCount == 0 { return []int{}, true diff --git a/euler_test.go b/euler_test.go index 193bd1c..60b694d 100644 --- a/euler_test.go +++ b/euler_test.go @@ -1,6 +1,7 @@ package graph import ( + "math/rand" "testing" ) @@ -155,3 +156,39 @@ func TestEulerUndirected(t *testing.T) { t.Errorf("EulerUndirected: %s", mess) } } + +func BenchmarkEulerDirected(b *testing.B) { + n := 100 + g := New(n) + for i := 0; i < n-1; i++ { + g.Add(i, i+1) + } + for i := 0; i < 3*n; i++ { + g.Add(rand.Intn(n), rand.Intn(n)) + } + h := Sort(g) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = EulerDirected(h) + } +} + +func BenchmarkEulerUndirected(b *testing.B) { + n := 100 + g := New(n) + for i := 0; i < n-1; i++ { + g.AddBoth(i, i+1) + } + for i := 0; i < 3*n; i++ { + g.Add(rand.Intn(n), rand.Intn(n)) + } + h := Sort(g) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = EulerUndirected(h) + } +} diff --git a/testdata/bench_after/EulerDirected b/testdata/bench_after/EulerDirected new file mode 100644 index 0000000..613ae4e --- /dev/null +++ b/testdata/bench_after/EulerDirected @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkEulerDirected-8 589666 2005 ns/op 960 B/op 4 allocs/op +PASS +ok github.com/rschio/graph 2.041s diff --git a/testdata/bench_after/EulerUndirected b/testdata/bench_after/EulerUndirected new file mode 100644 index 0000000..1c55a66 --- /dev/null +++ b/testdata/bench_after/EulerUndirected @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkEulerUndirected-8 508767 2387 ns/op 968 B/op 5 allocs/op +PASS +ok github.com/rschio/graph 2.188s diff --git a/testdata/bench_before/EulerDirected b/testdata/bench_before/EulerDirected new file mode 100644 index 0000000..8b1c551 --- /dev/null +++ b/testdata/bench_before/EulerDirected @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkEulerDirected-8 147134 7276 ns/op 5712 B/op 103 allocs/op +PASS +ok github.com/rschio/graph 1.159s diff --git a/testdata/bench_before/EulerUndirected b/testdata/bench_before/EulerUndirected new file mode 100644 index 0000000..490999f --- /dev/null +++ b/testdata/bench_before/EulerUndirected @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/rschio/graph +cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz +BenchmarkEulerUndirected-8 152704 7065 ns/op 5720 B/op 104 allocs/op +PASS +ok github.com/rschio/graph 1.164s diff --git a/testdata/bench_results/EulerDirected b/testdata/bench_results/EulerDirected new file mode 100644 index 0000000..0ef476c --- /dev/null +++ b/testdata/bench_results/EulerDirected @@ -0,0 +1,8 @@ +name old time/op new time/op delta +EulerDirected-8 7.28µs ± 0% 2.00µs ± 0% -72.44% + +name old alloc/op new alloc/op delta +EulerDirected-8 5.71kB ± 0% 0.96kB ± 0% -83.19% + +name old allocs/op new allocs/op delta +EulerDirected-8 103 ± 0% 4 ± 0% -96.12% diff --git a/testdata/bench_results/EulerUndirected b/testdata/bench_results/EulerUndirected new file mode 100644 index 0000000..c750c1c --- /dev/null +++ b/testdata/bench_results/EulerUndirected @@ -0,0 +1,8 @@ +name old time/op new time/op delta +EulerUndirected-8 7.07µs ± 0% 2.39µs ± 0% -66.21% + +name old alloc/op new alloc/op delta +EulerUndirected-8 5.72kB ± 0% 0.97kB ± 0% -83.08% + +name old allocs/op new allocs/op delta +EulerUndirected-8 104 ± 0% 5 ± 0% -95.19% From 77014c3c08e9dfddb35136cea9bb7ae89fe2931b Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 22:45:28 -0400 Subject: [PATCH 12/14] remove dir placeholder --- testdata/bench_after/placeholder | 0 testdata/bench_before/placeholder | 0 testdata/bench_results/placeholder | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 testdata/bench_after/placeholder delete mode 100644 testdata/bench_before/placeholder delete mode 100644 testdata/bench_results/placeholder diff --git a/testdata/bench_after/placeholder b/testdata/bench_after/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/testdata/bench_before/placeholder b/testdata/bench_before/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/testdata/bench_results/placeholder b/testdata/bench_results/placeholder deleted file mode 100644 index e69de29..0000000 From b4c6cd67960501b6b2b5c5a29cb1def2741f7fc8 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 22:46:48 -0400 Subject: [PATCH 13/14] Revert "MaxFlow bench after Sort -- TODO should update MaxFlow?" This reverts commit 52e36155619719f60a25f20f5aaabd136b59dafd. --- testdata/bench_after/MaxFlow | 4 ++-- testdata/bench_results/MaxFlow | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/testdata/bench_after/MaxFlow b/testdata/bench_after/MaxFlow index 0a87a50..5adc2ae 100644 --- a/testdata/bench_after/MaxFlow +++ b/testdata/bench_after/MaxFlow @@ -2,6 +2,6 @@ goos: linux goarch: amd64 pkg: github.com/rschio/graph cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz -BenchmarkMaxFlow-8 1 2086161963 ns/op 15496176 B/op 21334 allocs/op +BenchmarkMaxFlow-8 1 2062536970 ns/op 15380024 B/op 21500 allocs/op PASS -ok github.com/rschio/graph 2.108s +ok github.com/rschio/graph 2.087s diff --git a/testdata/bench_results/MaxFlow b/testdata/bench_results/MaxFlow index 7c3a027..ffbb379 100644 --- a/testdata/bench_results/MaxFlow +++ b/testdata/bench_results/MaxFlow @@ -1,8 +1,8 @@ name old time/op new time/op delta -MaxFlow-8 2.05s ± 0% 2.09s ± 0% +1.62% +MaxFlow-8 2.05s ± 0% 2.06s ± 0% +0.47% name old alloc/op new alloc/op delta -MaxFlow-8 15.6MB ± 0% 15.5MB ± 0% -0.48% +MaxFlow-8 15.6MB ± 0% 15.4MB ± 0% -1.22% name old allocs/op new allocs/op delta -MaxFlow-8 22.2k ± 0% 21.3k ± 0% -3.93% +MaxFlow-8 22.2k ± 0% 21.5k ± 0% -3.18% From 7f9d32b21e20894005bcd2f6c1f606f85a051567 Mon Sep 17 00:00:00 2001 From: rschio Date: Thu, 20 Oct 2022 22:50:31 -0400 Subject: [PATCH 14/14] Revert "MaxFlow memory improvement" This reverts commit 745e29b535c59d8891ff133751c6aa1de3ea1ab2, reversing changes made to 4ee17fc62955eedb59fc94965463110769791642. --- maxflow.go | 40 +++++++++++++++------------------- maxflow_test.go | 3 +-- testdata/bench_after/MaxFlow | 7 ------ testdata/bench_before/MaxFlow | 7 ------ testdata/bench_results/MaxFlow | 8 ------- 5 files changed, 18 insertions(+), 47 deletions(-) delete mode 100644 testdata/bench_after/MaxFlow delete mode 100644 testdata/bench_before/MaxFlow delete mode 100644 testdata/bench_results/MaxFlow diff --git a/maxflow.go b/maxflow.go index eb0d742..641db44 100644 --- a/maxflow.go +++ b/maxflow.go @@ -27,15 +27,13 @@ func MaxFlow(g Iterator, s, t int) (flow int64, graph Iterator) { } } res := New(n) - var v int - do := func(w int, c int64) (skip bool) { - if flow := c - residual.Cost(v, w); flow > 0 { - res.AddCost(v, w, flow) - } - return - } - for v = 0; v < n; v++ { - g.Visit(v, do) + for v := 0; v < n; v++ { + g.Visit(v, func(w int, c int64) (skip bool) { + if flow := c - residual.Cost(v, w); flow > 0 { + res.AddCost(v, w, flow) + } + return + }) } return flow, Sort(res) } @@ -43,21 +41,17 @@ func MaxFlow(g Iterator, s, t int) (flow int64, graph Iterator) { func residualFlow(g *Mutable, s, t int, prev []int) bool { visited := make([]bool, g.Order()) prev[s], visited[s] = -1, true - queue := []int{s} - - var v int - do := func(w int, c int64) (skip bool) { - if !visited[w] && c > 0 { - prev[w] = v - visited[w] = true - queue = append(queue, w) - } - return - } - for len(queue) > 0 { - v = queue[0] + for queue := []int{s}; len(queue) > 0; { + v := queue[0] queue = queue[1:] - g.Visit(v, do) + g.Visit(v, func(w int, c int64) (skip bool) { + if !visited[w] && c > 0 { + prev[w] = v + visited[w] = true + queue = append(queue, w) + } + return + }) } return visited[t] } diff --git a/maxflow_test.go b/maxflow_test.go index a018919..350d8a5 100644 --- a/maxflow_test.go +++ b/maxflow_test.go @@ -70,8 +70,7 @@ func TestMaxFlow(t *testing.T) { } func BenchmarkMaxFlow(b *testing.B) { - n := 500 - b.ReportAllocs() + n := 50 b.StopTimer() g := New(n) for i := 0; i < n; i++ { diff --git a/testdata/bench_after/MaxFlow b/testdata/bench_after/MaxFlow deleted file mode 100644 index 5adc2ae..0000000 --- a/testdata/bench_after/MaxFlow +++ /dev/null @@ -1,7 +0,0 @@ -goos: linux -goarch: amd64 -pkg: github.com/rschio/graph -cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz -BenchmarkMaxFlow-8 1 2062536970 ns/op 15380024 B/op 21500 allocs/op -PASS -ok github.com/rschio/graph 2.087s diff --git a/testdata/bench_before/MaxFlow b/testdata/bench_before/MaxFlow deleted file mode 100644 index 2a674e0..0000000 --- a/testdata/bench_before/MaxFlow +++ /dev/null @@ -1,7 +0,0 @@ -goos: linux -goarch: amd64 -pkg: github.com/rschio/graph -cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz -BenchmarkMaxFlow-8 1 2052845414 ns/op 15570528 B/op 22207 allocs/op -PASS -ok github.com/rschio/graph 2.073s diff --git a/testdata/bench_results/MaxFlow b/testdata/bench_results/MaxFlow deleted file mode 100644 index ffbb379..0000000 --- a/testdata/bench_results/MaxFlow +++ /dev/null @@ -1,8 +0,0 @@ -name old time/op new time/op delta -MaxFlow-8 2.05s ± 0% 2.06s ± 0% +0.47% - -name old alloc/op new alloc/op delta -MaxFlow-8 15.6MB ± 0% 15.4MB ± 0% -1.22% - -name old allocs/op new allocs/op delta -MaxFlow-8 22.2k ± 0% 21.5k ± 0% -3.18%