commit ded4a6b87dee2a50c2eff9a1552e461dfa0e2e98 Author: shenzhuan Date: Wed Nov 30 14:43:55 2022 +0800 first commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/edu_demo.iml b/.idea/edu_demo.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/edu_demo.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..12369ca --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cpu.prof b/cpu.prof new file mode 100644 index 0000000..68dffda Binary files /dev/null and b/cpu.prof differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..351f136 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module edu + +go 1.19 diff --git a/main.exe b/main.exe new file mode 100644 index 0000000..5aabb08 Binary files /dev/null and b/main.exe differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..f94725e --- /dev/null +++ b/main.go @@ -0,0 +1,777 @@ +// Go from multi-language-benchmark/src/havlak/go_pro + +// Copyright 2011 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Test Program for the Havlak loop finder. +// +// This program constructs a fairly large control flow +// graph and performs loop recognition. This is the Go +// version. +// +package main + +import ( + "flag" + "fmt" + "log" + "os" + "runtime/pprof" +) + +type BasicBlock struct { + Name int + InEdges []*BasicBlock + OutEdges []*BasicBlock +} + +func NewBasicBlock(name int) *BasicBlock { + return &BasicBlock{Name: name} +} + +func (bb *BasicBlock) Dump() { + fmt.Printf("BB#%06d:", bb.Name) + if len(bb.InEdges) > 0 { + fmt.Printf(" in :") + for _, iter := range bb.InEdges { + fmt.Printf(" BB#%06d", iter.Name) + } + } + if len(bb.OutEdges) > 0 { + fmt.Print(" out:") + for _, iter := range bb.OutEdges { + fmt.Printf(" BB#%06d", iter.Name) + } + } + fmt.Printf("\n") +} + +func (bb *BasicBlock) NumPred() int { + return len(bb.InEdges) +} + +func (bb *BasicBlock) NumSucc() int { + return len(bb.OutEdges) +} + +func (bb *BasicBlock) AddInEdge(from *BasicBlock) { + bb.InEdges = append(bb.InEdges, from) +} + +func (bb *BasicBlock) AddOutEdge(to *BasicBlock) { + bb.OutEdges = append(bb.OutEdges, to) +} + +//----------------------------------------------------------- + +type CFG struct { + Blocks []*BasicBlock + Start *BasicBlock +} + +func NewCFG() *CFG { + return &CFG{} +} + +func (cfg *CFG) NumNodes() int { + return len(cfg.Blocks) +} + +func (cfg *CFG) CreateNode(node int) *BasicBlock { + if node < len(cfg.Blocks) { + return cfg.Blocks[node] + } + if node != len(cfg.Blocks) { + println("oops", node, len(cfg.Blocks)) + panic("wtf") + } + bblock := NewBasicBlock(node) + cfg.Blocks = append(cfg.Blocks, bblock) + + if len(cfg.Blocks) == 1 { + cfg.Start = bblock + } + + return bblock +} + +func (cfg *CFG) Dump() { + for _, n := range cfg.Blocks { + n.Dump() + } +} + +//----------------------------------------------------------- + +type BasicBlockEdge struct { + Dst *BasicBlock + Src *BasicBlock +} + +func NewBasicBlockEdge(cfg *CFG, from int, to int) *BasicBlockEdge { + self := new(BasicBlockEdge) + self.Src = cfg.CreateNode(from) + self.Dst = cfg.CreateNode(to) + + self.Src.AddOutEdge(self.Dst) + self.Dst.AddInEdge(self.Src) + + return self +} + +//----------------------------------------------------------- +// Basic Blocks and Loops are being classified as regular, irreducible, +// and so on. This enum contains a symbolic name for all these classifications +// +const ( + _ = iota // Go has an interesting iota concept + bbTop // uninitialized + bbNonHeader // a regular BB + bbReducible // reducible loop + bbSelf // single BB loop + bbIrreducible // irreducible loop + bbDead // a dead BB + bbLast // sentinel +) + +// UnionFindNode is used in the Union/Find algorithm to collapse +// complete loops into a single node. These nodes and the +// corresponding functionality are implemented with this class +// +type UnionFindNode struct { + parent *UnionFindNode + bb *BasicBlock + loop *SimpleLoop + dfsNumber int +} + +// Init explicitly initializes UnionFind nodes. +// +func (u *UnionFindNode) Init(bb *BasicBlock, dfsNumber int) { + u.parent = u + u.bb = bb + u.dfsNumber = dfsNumber + u.loop = nil +} + +// FindSet implements the Find part of the Union/Find Algorithm +// +// Implemented with Path Compression (inner loops are only +// visited and collapsed once, however, deep nests would still +// result in significant traversals). +// +func (u *UnionFindNode) FindSet() *UnionFindNode { + var nodeList []*UnionFindNode + node := u + + for ; node != node.parent; node = node.parent { + if node.parent != node.parent.parent { + nodeList = append(nodeList, node) + } + + } + + // Path Compression, all nodes' parents point to the 1st level parent. + for _, ll := range nodeList { + ll.parent = node.parent + } + + return node +} + +// Union relies on path compression. +// +func (u *UnionFindNode) Union(B *UnionFindNode) { + u.parent = B +} + +// Constants +// +// Marker for uninitialized nodes. +const unvisited = -1 + +// Safeguard against pathological algorithm behavior. +const maxNonBackPreds = 32 * 1024 + +// IsAncestor +// +// As described in the paper, determine whether a node 'w' is a +// "true" ancestor for node 'v'. +// +// Dominance can be tested quickly using a pre-order trick +// for depth-first spanning trees. This is why DFS is the first +// thing we run below. +// +// Go comment: Parameters can be written as w,v int, inlike in C, where +// each parameter needs its own type. +// +func isAncestor(w, v int, last []int) bool { + return ((w <= v) && (v <= last[w])) +} + +// listContainsNode +// +// Check whether a list contains a specific element. +// +func listContainsNode(l []*UnionFindNode, u *UnionFindNode) bool { + for _, ll := range l { + if ll == u { + return true + } + } + return false +} + +// DFS - Depth-First-Search and node numbering. +// Step a: +//func DFS(currentNode *BasicBlock, nodes []*UnionFindNode, number map[*BasicBlock]int, last []int, current int) int { +//// step b: +// nodes[current].Init(currentNode, current) +// number[currentNode] = current +// +// lastid := current +// for _, target := range currentNode.OutEdges { +// if number[target] == unvisited { +// lastid = DFS(target, nodes, number, last, lastid+1) +// } +// } +// last[number[currentNode]] = lastid +// return lastid +//} +// Step b: +func DFS(currentNode *BasicBlock, nodes []*UnionFindNode, number []int, last []int, current int) int { + nodes[current].Init(currentNode, current) + number[currentNode.Name] = current + + lastid := current + for _, target := range currentNode.OutEdges { + if number[target.Name] == unvisited { + lastid = DFS(target, nodes, number, last, lastid+1) + } + } + last[number[currentNode.Name]] = lastid + return lastid +} + +// step d 所需 +func appendUnique(a []int, x int) []int { + for _, y := range a { + if x == y { + return a + } + } + return append(a, x) +} + +// FindLoops +// +// Find loops and build loop forest using Havlak's algorithm, which +// is derived from Tarjan. Variable names and step numbering has +// been chosen to be identical to the nomenclature in Havlak's +// paper (which, in turn, is similar to the one used by Tarjan). +// +func FindLoops(cfgraph *CFG, lsgraph *LSG) { + if cfgraph.Start == nil { + return + } + + size := cfgraph.NumNodes() + //step a b c + //nonBackPreds := make([]map[int]bool, size) + //step d + nonBackPreds := make([][]int, size) + backPreds := make([][]int, size) + //step a: + //number := make(map[*BasicBlock]int) + //step b: + number := make([]int, size) + header := make([]int, size, size) + types := make([]int, size, size) + last := make([]int, size, size) + nodes := make([]*UnionFindNode, size, size) + + for i := 0; i < size; i++ { + nodes[i] = new(UnionFindNode) + } + + // Step a:for _, bb := range cfgraph.Blocks { + // - initialize all nodes as unvisited. + // - depth-first traversal and numbering. + // - unreached BB's are marked as dead. + // + //step d for i,bb 改成 for _,bb + for _, bb := range cfgraph.Blocks { + // Step a: + //number[bb] = unvisited + // Step b: + number[bb.Name] = unvisited + //step d 注释下面行 + //nonBackPreds[i] = make(map[int]bool) + + } + + //Step a: + DFS(cfgraph.Start, nodes, number, last, 0) + // A backedge comes from a descendant in the DFS tree, and non-backedges + // from non-descendants (following Tarjan). + // + // - check incoming edges 'v' and add them to either + // - the list of backedges (backPreds) or + // - the list of non-backedges (nonBackPreds) + // + for w := 0; w < size; w++ { + header[w] = 0 + types[w] = bbNonHeader + + nodeW := nodes[w].bb + if nodeW == nil { + types[w] = bbDead + continue // dead BB + } + + if nodeW.NumPred() > 0 { + for _, nodeV := range nodeW.InEdges { + //step a: + //v := number[nodeV] + //step b: + v := number[nodeV.Name] + if v == unvisited { + continue // dead node + } + + if isAncestor(w, v, last) { + backPreds[w] = append(backPreds[w], v) + } else { + // step abc + // nonBackPreds[w][v] = true + //step d + nonBackPreds[w] = appendUnique(nonBackPreds[w], v) + } + } + } + } + + // Start node is root of all other loops. + header[0] = 0 + + // Step c: + // + // The outer loop, unchanged from Tarjan. It does nothing except + // for those nodes which are the destinations of backedges. + // For a header node w, we chase backward from the sources of the + // backedges adding nodes to the set P, representing the body of + // the loop headed by w. + // + // By running through the nodes in reverse of the DFST preorder, + // we ensure that inner loop headers will be processed before the + // headers for surrounding loops. + // + for w := size - 1; w >= 0; w-- { + // this is 'P' in Havlak's paper + var nodePool []*UnionFindNode + + nodeW := nodes[w].bb + if nodeW == nil { + continue // dead BB + } + + // Step d: + for _, v := range backPreds[w] { + if v != w { + nodePool = append(nodePool, nodes[v].FindSet()) + } else { + types[w] = bbSelf + } + } + + // Copy nodePool to workList. + // + workList := append([]*UnionFindNode(nil), nodePool...) + + if len(nodePool) != 0 { + types[w] = bbReducible + } + + // work the list... + // + for len(workList) > 0 { + x := workList[0] + workList = workList[1:] + + // Step e: + // + // Step e represents the main difference from Tarjan's method. + // Chasing upwards from the sources of a node w's backedges. If + // there is a node y' that is not a descendant of w, w is marked + // the header of an irreducible loop, there is another entry + // into this loop that avoids w. + // + + // The algorithm has degenerated. Break and + // return in this case. + // + nonBackSize := len(nonBackPreds[x.dfsNumber]) + if nonBackSize > maxNonBackPreds { + return + } + + for iter := range nonBackPreds[x.dfsNumber] { + y := nodes[iter] + ydash := y.FindSet() + + if !isAncestor(w, ydash.dfsNumber, last) { + types[w] = bbIrreducible + //step abc + //nonBackPreds[w][ydash.dfsNumber] = true + //step d + nonBackPreds[w] = appendUnique(nonBackPreds[w], ydash.dfsNumber) + } else { + if ydash.dfsNumber != w { + if !listContainsNode(nodePool, ydash) { + workList = append(workList, ydash) + nodePool = append(nodePool, ydash) + } + } + } + } + } + + // Collapse/Unionize nodes in a SCC to a single node + // For every SCC found, create a loop descriptor and link it in. + // + if (len(nodePool) > 0) || (types[w] == bbSelf) { + loop := lsgraph.NewLoop() + + loop.SetHeader(nodeW) + if types[w] != bbIrreducible { + loop.IsReducible = true + } + + // At this point, one can set attributes to the loop, such as: + // + // the bottom node: + // iter = backPreds[w].begin(); + // loop bottom is: nodes[iter].node); + // + // the number of backedges: + // backPreds[w].size() + // + // whether this loop is reducible: + // type[w] != BasicBlockClass.bbIrreducible + // + nodes[w].loop = loop + + for _, node := range nodePool { + // Add nodes to loop descriptor. + header[node.dfsNumber] = w + node.Union(nodes[w]) + + // Nested loops are not added, but linked together. + if node.loop != nil { + node.loop.Parent = loop + } else { + loop.AddNode(node.bb) + } + } + + lsgraph.AddLoop(loop) + } // nodePool.size + } // Step c + +} + +// External entry point. +func FindHavlakLoops(cfgraph *CFG, lsgraph *LSG) int { + FindLoops(cfgraph, lsgraph) + return lsgraph.NumLoops() +} + +//====================================================== +// Scaffold Code +//====================================================== + +// Basic representation of loops, a loop has an entry point, +// one or more exit edges, a set of basic blocks, and potentially +// an outer loop - a "parent" loop. +// +// Furthermore, it can have any set of properties, e.g., +// it can be an irreducible loop, have control flow, be +// a candidate for transformations, and what not. +// +type SimpleLoop struct { + // No set, use map to bool + basicBlocks map[*BasicBlock]bool + Children map[*SimpleLoop]bool + Parent *SimpleLoop + header *BasicBlock + + IsRoot bool + IsReducible bool + Counter int + NestingLevel int + DepthLevel int +} + +func (loop *SimpleLoop) AddNode(bb *BasicBlock) { + loop.basicBlocks[bb] = true +} + +func (loop *SimpleLoop) AddChildLoop(child *SimpleLoop) { + loop.Children[child] = true +} + +func (loop *SimpleLoop) Dump(indent int) { + for i := 0; i < indent; i++ { + fmt.Printf(" ") + } + + // No ? operator ? + fmt.Printf("loop-%d nest: %d depth %d ", + loop.Counter, loop.NestingLevel, loop.DepthLevel) + if !loop.IsReducible { + fmt.Printf("(Irreducible) ") + } + + // must have > 0 + if len(loop.Children) > 0 { + fmt.Printf("Children: ") + for ll := range loop.Children { + fmt.Printf("loop-%d", ll.Counter) + } + } + if len(loop.basicBlocks) > 0 { + fmt.Printf("(") + for bb := range loop.basicBlocks { + fmt.Printf("BB#%06d ", bb.Name) + if loop.header == bb { + fmt.Printf("*") + } + } + fmt.Printf("\b)") + } + fmt.Printf("\n") +} + +func (loop *SimpleLoop) SetParent(parent *SimpleLoop) { + loop.Parent = parent + loop.Parent.AddChildLoop(loop) +} + +func (loop *SimpleLoop) SetHeader(bb *BasicBlock) { + loop.AddNode(bb) + loop.header = bb +} + +//------------------------------------ +// Helper (No templates or such) +// +func max(x, y int) int { + if x > y { + return x + } + return y +} + +// LoopStructureGraph +// +// Maintain loop structure for a given CFG. +// +// Two values are maintained for this loop graph, depth, and nesting level. +// For example: +// +// loop nesting level depth +//---------------------------------------- +// loop-0 2 0 +// loop-1 1 1 +// loop-3 1 1 +// loop-2 0 2 +// +var loopCounter = 0 + +type LSG struct { + root *SimpleLoop + loops []*SimpleLoop +} + +func NewLSG() *LSG { + lsg := new(LSG) + lsg.root = lsg.NewLoop() + lsg.root.NestingLevel = 0 + + return lsg +} + +func (lsg *LSG) NewLoop() *SimpleLoop { + loop := new(SimpleLoop) + loop.basicBlocks = make(map[*BasicBlock]bool) + loop.Children = make(map[*SimpleLoop]bool) + loop.Parent = nil + loop.header = nil + + loop.Counter = loopCounter + loopCounter++ + return loop +} + +func (lsg *LSG) AddLoop(loop *SimpleLoop) { + lsg.loops = append(lsg.loops, loop) +} + +func (lsg *LSG) Dump() { + lsg.dump(lsg.root, 0) +} + +func (lsg *LSG) dump(loop *SimpleLoop, indent int) { + loop.Dump(indent) + + for ll := range loop.Children { + lsg.dump(ll, indent+1) + } +} + +func (lsg *LSG) CalculateNestingLevel() { + for _, sl := range lsg.loops { + if sl.IsRoot { + continue + } + if sl.Parent == nil { + sl.SetParent(lsg.root) + } + } + lsg.calculateNestingLevel(lsg.root, 0) +} + +func (lsg *LSG) calculateNestingLevel(loop *SimpleLoop, depth int) { + loop.DepthLevel = depth + for ll := range loop.Children { + lsg.calculateNestingLevel(ll, depth+1) + + ll.NestingLevel = max(loop.NestingLevel, ll.NestingLevel+1) + } +} + +func (lsg *LSG) NumLoops() int { + return len(lsg.loops) +} + +func (lsg *LSG) Root() *SimpleLoop { + return lsg.root +} + +//====================================================== +// Testing Code +//====================================================== + +func buildDiamond(cfgraph *CFG, start int) int { + bb0 := start + NewBasicBlockEdge(cfgraph, bb0, bb0+1) + NewBasicBlockEdge(cfgraph, bb0, bb0+2) + NewBasicBlockEdge(cfgraph, bb0+1, bb0+3) + NewBasicBlockEdge(cfgraph, bb0+2, bb0+3) + + return bb0 + 3 +} + +func buildConnect(cfgraph *CFG, start int, end int) { + NewBasicBlockEdge(cfgraph, start, end) +} + +func buildStraight(cfgraph *CFG, start int, n int) int { + for i := 0; i < n; i++ { + buildConnect(cfgraph, start+i, start+i+1) + } + return start + n +} + +func buildBaseLoop(cfgraph *CFG, from int) int { + header := buildStraight(cfgraph, from, 1) + diamond1 := buildDiamond(cfgraph, header) + d11 := buildStraight(cfgraph, diamond1, 1) + diamond2 := buildDiamond(cfgraph, d11) + footer := buildStraight(cfgraph, diamond2, 1) + buildConnect(cfgraph, diamond2, d11) + buildConnect(cfgraph, diamond1, header) + + buildConnect(cfgraph, footer, from) + footer = buildStraight(cfgraph, footer, 1) + return footer +} + +//step a b 接收参数 存储cpu信息 修改成默认存储文件 +var cpuprofile = flag.String("cpuprofile", "cpu.prof", "write cpu profile to this file") + +//step c 存储内存信息 +var memprofile = flag.String("memprofile", "mem.prof", "write memory profile to this file") + +func main() { + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + lsgraph := NewLSG() + cfgraph := NewCFG() + + cfgraph.CreateNode(0) // top + cfgraph.CreateNode(1) // bottom + NewBasicBlockEdge(cfgraph, 0, 2) + + for dummyloop := 0; dummyloop < 15000; dummyloop++ { + FindHavlakLoops(cfgraph, NewLSG()) + } + + n := 2 + + for parlooptrees := 0; parlooptrees < 10; parlooptrees++ { + cfgraph.CreateNode(n + 1) + buildConnect(cfgraph, 2, n+1) + n = n + 1 + + for i := 0; i < 100; i++ { + top := n + n = buildStraight(cfgraph, n, 1) + for j := 0; j < 25; j++ { + n = buildBaseLoop(cfgraph, n) + } + bottom := buildStraight(cfgraph, n, 1) + buildConnect(cfgraph, n, top) + n = bottom + } + buildConnect(cfgraph, n, 1) + } + + FindHavlakLoops(cfgraph, lsgraph) + + //step c 记录内存 + if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal(err) + } + pprof.WriteHeapProfile(f) + f.Close() + return + } + for i := 0; i < 50; i++ { + FindHavlakLoops(cfgraph, NewLSG()) + } + + fmt.Printf("# of loops: %d (including 1 artificial root node)\n", lsgraph.NumLoops()) + lsgraph.CalculateNestingLevel() +} diff --git a/mem.prof b/mem.prof new file mode 100644 index 0000000..3de3a58 Binary files /dev/null and b/mem.prof differ