From c6d33c500d4b32abb6d184fed7ef182a765b6b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=98=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Fri, 13 Sep 2024 13:11:29 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=BA=D0=BE=D0=BD=D1=87=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B1=D0=B5=D0=BD=D1=87=D0=BC=D0=B0=D1=80=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cut_the_tree/funcs.go | 22 +-- cut_the_tree/funcs_bench_test.go | 25 --- cut_the_tree/funcs_test.go | 65 +++++--- cut_the_tree/test_models.go | 252 ++++--------------------------- go.mod | 2 +- go.sum | 2 + 6 files changed, 94 insertions(+), 274 deletions(-) delete mode 100644 cut_the_tree/funcs_bench_test.go diff --git a/cut_the_tree/funcs.go b/cut_the_tree/funcs.go index bfe3fb4..1fdd904 100644 --- a/cut_the_tree/funcs.go +++ b/cut_the_tree/funcs.go @@ -17,19 +17,19 @@ type WeightedNode struct { weight uint16 } -func (root Node) WeighingTreeWithRecursion() map[string]uint16 { - weight := getNodeWeight(root.ID, root.Name) +func (root Node) WeightingTreeWithRecursion() map[string]uint16 { + weight := root.getNodeWeight() var res = make(map[string]uint16) for _, v := range root.Children { - maps.Copy(res, v.WeighingTreeWithRecursion()) // Т.е. в go нет оптимизации хвостовой рекурсии, это породит неимоверное кол-во аллокаций. + maps.Copy(res, v.WeightingTreeWithRecursion()) // Т.е. в go нет оптимизации хвостовой рекурсии, это породит неимоверное кол-во аллокаций. weight = weight + res[v.ID] } res[root.ID] = weight return res } -func (root Node) WeighingTreeWithStack() map[string]uint16 { - weight := getNodeWeight(root.ID, root.Name) +func (root Node) WeightingTreeWithStack() map[string]uint16 { + weight := root.getNodeWeight() counter := map[string]*WeightedNode{root.ID: { weight: weight, }} @@ -38,7 +38,7 @@ func (root Node) WeighingTreeWithStack() map[string]uint16 { current := stack[len(stack)-1] stack = stack[:len(stack)-1] - nodeWeight := getNodeWeight(current.ID, current.Name) + nodeWeight := current.getNodeWeight() counter[current.ID].weight = nodeWeight // Прибавляем вес всем родительским нодам до корня parentID := counter[current.ID].parent @@ -68,7 +68,7 @@ func (root Node) WeighingTreeWithStack() map[string]uint16 { } func (root Node) WeightingTreeWithDLL() map[string]uint16 { - weight := getNodeWeight(root.ID, root.Name) + weight := root.getNodeWeight() counter := map[string]*WeightedNode{root.ID: { weight: weight, }} @@ -79,7 +79,7 @@ func (root Node) WeightingTreeWithDLL() map[string]uint16 { current := dllist.Front() dllist.Remove(current) - nodeWeight := getNodeWeight(current.Value.(Node).ID, current.Value.(Node).Name) + nodeWeight := current.Value.(Node).getNodeWeight() counter[current.Value.(Node).ID].weight = nodeWeight // Прибавляем вес всем родительским нодам до корня parentID := counter[current.Value.(Node).ID].parent @@ -113,10 +113,10 @@ func (root Node) DecomposeTree(weights map[string]uint16, limit int) ([]*Node, e return nil, nil } -func getNodeWeight(id string, name string) uint16 { +func (n Node) getNodeWeight() uint16 { nodeBytes, _ := json.Marshal(Node{ - ID: id, - Name: name, + ID: n.ID, + Name: n.Name, }) return uint16(len(nodeBytes)) } diff --git a/cut_the_tree/funcs_bench_test.go b/cut_the_tree/funcs_bench_test.go deleted file mode 100644 index 11c2ee3..0000000 --- a/cut_the_tree/funcs_bench_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func BenchmarkNode_WeighingSmallOrdinaryTreeWithRecursion(b *testing.B) { - for i := 0; i < b.N; i++ { - assert.EqualValues(b, testOrdinaryVFS.Weghts, testOrdinaryVFS.Tree.WeighingTreeWithRecursion()) - } -} - -func BenchmarkNode_WeighingSmallOrdinaryTreeWithStack(b *testing.B) { - for i := 0; i < b.N; i++ { - assert.EqualValues(b, testOrdinaryVFS.Weghts, testOrdinaryVFS.Tree.WeighingTreeWithStack()) - } -} - -func BenchmarkNode_WeighingSmallOrdinaryTreeWithDLL(b *testing.B) { - for i := 0; i < b.N; i++ { - assert.EqualValues(b, testOrdinaryVFS.Weghts, testOrdinaryVFS.Tree.WeightingTreeWithDLL()) - } -} diff --git a/cut_the_tree/funcs_test.go b/cut_the_tree/funcs_test.go index 22f0d4a..c92226b 100644 --- a/cut_the_tree/funcs_test.go +++ b/cut_the_tree/funcs_test.go @@ -1,29 +1,21 @@ package main import ( - "testing" - "github.com/stretchr/testify/assert" +"github.com/stretchr/testify/assert" +"testing" ) + func TestNode_WeighingTreeAllAlgo(t *testing.T) { - for name,tt := range map[string]struct{ - input Node - output map[string]uint16 - }{ - "Ordinary VFS tree":{ - input:testOrdinaryVFS.Tree, - output: testOrdinaryVFS.Weghts, - }, - "VFS in the form of a pathological tree":{ - input: testPathologicalTree.Tree, - output: testPathologicalTree.Weghts, - }, + for name,tt := range map[string]TestTree{ + "Ordinary VFS tree": getTestTree(4, 4), + "VFS in the form of a pathological tree":getTestTree(1, 100), }{ t.Run(name,func(t *testing.T) { - assert.EqualValues(t, tt.output, tt.input.WeighingTreeWithRecursion()) - assert.EqualValues(t, tt.output, tt.input.WeighingTreeWithStack()) - assert.EqualValues(t, tt.output, tt.input.WeightingTreeWithDLL()) + assert.EqualValues(t, tt.Weights, tt.Tree.WeightingTreeWithRecursion()) + assert.EqualValues(t, tt.Weights, tt.Tree.WeightingTreeWithStack()) + assert.EqualValues(t, tt.Weights, tt.Tree.WeightingTreeWithDLL()) }) } } @@ -31,3 +23,42 @@ func TestNode_WeighingTreeAllAlgo(t *testing.T) { func TestNode_DecomposeTree(t *testing.T) { } + +func Benchmark(b *testing.B) { + b.StopTimer() + b.ResetTimer() + for name,tt := range map[string]struct{ + branches int + depth int + }{ + "Small tree":{4, 4}, // 85 nodes, + "Wide tree": {515, 3}, // 265'741 nodes, + "Deep tree": {3, 12}, // 265'720 nodes, + "Huge tree": {5, 10}, // 2'441'406 nodes, + "Pathological tree": {1, 10000}, // 10'000 nodes, + }{ + b.Run(name, func(b *testing.B) { + tree := getTestTree(tt.branches, tt.depth) + b.Cleanup(func() { + tree = TestTree{nil, nil} + }) + b.Run("Weighing with recursion", func(b *testing.B) { + b.StartTimer() + assert.EqualValues(b, tree.Weights, tree.Tree.WeightingTreeWithRecursion()) + b.StopTimer() + }) + b.Run("Weighting with stack", func(b *testing.B) { + b.StartTimer() + assert.EqualValues(b, tree.Weights, tree.Tree.WeightingTreeWithStack()) + b.StopTimer() + }) + b.Run("Weighting with DLL", func(b *testing.B) { + b.StartTimer() + assert.EqualValues(b, tree.Weights, tree.Tree.WeightingTreeWithDLL()) + b.StopTimer() + }) + }) + + } +} + diff --git a/cut_the_tree/test_models.go b/cut_the_tree/test_models.go index 44b705c..246cdd3 100644 --- a/cut_the_tree/test_models.go +++ b/cut_the_tree/test_models.go @@ -1,227 +1,39 @@ package main +import ( + "github.com/google/uuid" + "maps" +) + type TestTree struct { - Tree Node - Weghts map[string]uint16 + Tree *Node + Weights map[string]uint16 } -var testOrdinaryVFS = TestTree{ - Tree: Node{ - ID: "node0", - Name: "node0", - Children: []*Node{ - { - ID: "node01", - Name: "node01", - Children: []*Node{ - { - ID: "node011", - Name: "node011", - Children: nil, - }, - { - ID: "node012", - Name: "node012", - Children: nil, - }, - { - ID: "node013", - Name: "node013", - Children: nil, - }, - }, - }, - { - ID: "node02", - Name: "node02", - Children: []*Node{ - { - ID: "node021", - Name: "node021", - Children: nil, - }, - { - ID: "node022", - Name: "node022", - Children: nil, - }, - { - ID: "node023", - Name: "node023", - Children: nil, - }, - }, - }, - { - ID: "node03", - Name: "node03", - Children: []*Node{ - { - ID: "node031", - Name: "node031", - Children: []*Node{ - { - ID: "node0311", - Name: "node0311", - Children: []*Node{ - { - ID: "node03111", - Name: "node03111", - Children: nil, - }, - { - ID: "node03112", - Name: "node03112", - Children: nil, - }, - { - ID: "node03113", - Name: "node03113", - Children: nil, - }, - }, - }, - { - ID: "node0312", - Name: "node0312", - Children: nil, - }, - { - ID: "node0313", - Name: "node0313", - Children: nil, - }, - }, - }, - { - ID: "node032", - Name: "node032", - Children: nil, - }, - { - ID: "node033", - Name: "node033", - Children: []*Node{ - { - ID: "node0331", - Name: "node0331", - Children: nil, - }, - { - ID: "node0332", - Name: "node0332", - Children: []*Node{ - { - ID: "node03321", - Name: "node03321", - Children: nil, - }, - { - ID: "node03322", - Name: "node03322", - Children: nil, - }, - { - ID: "node03323", - Name: "node03323", - Children: []*Node{ - { - ID: "node033231", - Name: "node033231", - Children: nil, - }, - { - ID: "node033232", - Name: "node033232", - Children: nil, - }, - { - ID: "node033233", - Name: "node033233", - Children: nil, - }, - }, - }, - }, - }, - { - ID: "node0333", - Name: "node0333", - Children: nil, - }, - }, - }, - }, - }, - }, - }, - Weghts: map[string]uint16{"node0": 1416, "node01": 194, "node011": 49, "node012": 49, "node013": 49, "node02": 194, "node021": 49, "node022": 49, "node023": 49, "node03": 983, "node031": 361, "node0311": 210, "node03111": 53, "node03112": 53, "node03113": 53, "node0312": 51, "node0313": 51, "node032": 49, "node033": 526, "node0331": 51, "node0332": 375, "node03321": 53, "node03322": 53, "node03323": 218, "node033231": 55, "node033232": 55, "node033233": 55, "node0333": 51}, -} +func getTestTree(branches, depth int) TestTree { + if depth == 0 { + return TestTree{} + } + id := uuid.NewString() + node := &Node{ + ID: id, + Name: id, + } + tree := TestTree{ + Tree: node, + Weights: map[string]uint16{id: node.getNodeWeight()}, + } -var testPathologicalTree = TestTree{ - Tree: Node{ - ID: "node0", - Name: "node0", - Children: []*Node{ - { - ID: "node01", - Name: "node01", - Children: []*Node{ - { - ID: "node012", - Name: "node012", - Children: []*Node{ - { - ID: "node0123", - Name: "node0123", - Children: []*Node{ - { - ID: "node01234", - Name: "node01234", - Children: []*Node{ - { - ID: "node012345", - Name: "node012345", - Children: []*Node{ - { - ID: "node0123456", - Name: "node0123456", - Children: []*Node{ - { - ID: "node01234567", - Name: "node01234567", - Children: []*Node{ - { - ID: "node012345678", - Name: "node012345678", - Children: []*Node{ - { - ID: "node0123456789", - Name: "node0123456789", - Children: []*Node{ - { - ID: "node0123456789A", - Name: "node0123456789A", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Weghts: map[string]uint16{"node0": 605, "node01": 560, "node012": 513, "node0123": 464, "node01234": 413, "node012345": 360, "node0123456": 305, "node01234567": 248, "node012345678": 189, "node0123456789": 128, "node0123456789A": 65}, + for i := 0; i < branches; i++ { + childTree := getTestTree(branches, depth-1) + if childTree.Tree != nil { + tree.Tree.Children = append(tree.Tree.Children, childTree.Tree) + maps.Copy(tree.Weights, childTree.Weights) + } + } + for _, child := range tree.Tree.Children { + tree.Weights[id] = tree.Weights[id] + tree.Weights[child.ID] + } + + return tree } diff --git a/go.mod b/go.mod index 4a9bfde..906ff1a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/codingsince1985/checksum v1.3.0 - github.com/mitchellh/go-homedir v1.1.0 + github.com/google/uuid v1.6.0 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.27.0 ) diff --git a/go.sum b/go.sum index 5fb8ad6..1de8b16 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/codingsince1985/checksum v1.3.0/go.mod h1:QfRskdtdWap+gJil8e5obw6I8/c github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=