github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/core/blockstm/dag.go (about) 1 package blockstm 2 3 import ( 4 "fmt" 5 "strings" 6 "time" 7 8 "github.com/heimdalr/dag" 9 10 "github.com/ethereum/go-ethereum/log" 11 ) 12 13 type DAG struct { 14 *dag.DAG 15 } 16 17 type TxDep struct { 18 Index int 19 ReadList []ReadDescriptor 20 FullWriteList [][]WriteDescriptor 21 } 22 23 func HasReadDep(txFrom TxnOutput, txTo TxnInput) bool { 24 reads := make(map[Key]bool) 25 26 for _, v := range txTo { 27 reads[v.Path] = true 28 } 29 30 for _, rd := range txFrom { 31 if _, ok := reads[rd.Path]; ok { 32 return true 33 } 34 } 35 36 return false 37 } 38 39 func BuildDAG(deps TxnInputOutput) (d DAG) { 40 d = DAG{dag.NewDAG()} 41 ids := make(map[int]string) 42 43 for i := len(deps.inputs) - 1; i > 0; i-- { 44 txTo := deps.inputs[i] 45 46 var txToId string 47 48 if _, ok := ids[i]; ok { 49 txToId = ids[i] 50 } else { 51 txToId, _ = d.AddVertex(i) 52 ids[i] = txToId 53 } 54 55 for j := i - 1; j >= 0; j-- { 56 txFrom := deps.allOutputs[j] 57 58 if HasReadDep(txFrom, txTo) { 59 var txFromId string 60 if _, ok := ids[j]; ok { 61 txFromId = ids[j] 62 } else { 63 txFromId, _ = d.AddVertex(j) 64 ids[j] = txFromId 65 } 66 67 err := d.AddEdge(txFromId, txToId) 68 if err != nil { 69 log.Warn("Failed to add edge", "from", txFromId, "to", txToId, "err", err) 70 } 71 } 72 } 73 } 74 75 return 76 } 77 78 func depsHelper(dependencies map[int]map[int]bool, txFrom TxnOutput, txTo TxnInput, i int, j int) map[int]map[int]bool { 79 if HasReadDep(txFrom, txTo) { 80 dependencies[i][j] = true 81 82 for k := range dependencies[i] { 83 _, foundDep := dependencies[j][k] 84 85 if foundDep { 86 delete(dependencies[i], k) 87 } 88 } 89 } 90 91 return dependencies 92 } 93 94 func UpdateDeps(deps map[int]map[int]bool, t TxDep) map[int]map[int]bool { 95 txTo := t.ReadList 96 97 deps[t.Index] = map[int]bool{} 98 99 for j := 0; j <= t.Index-1; j++ { 100 txFrom := t.FullWriteList[j] 101 102 deps = depsHelper(deps, txFrom, txTo, t.Index, j) 103 } 104 105 return deps 106 } 107 108 func GetDep(deps TxnInputOutput) map[int]map[int]bool { 109 newDependencies := map[int]map[int]bool{} 110 111 for i := 1; i < len(deps.inputs); i++ { 112 txTo := deps.inputs[i] 113 114 newDependencies[i] = map[int]bool{} 115 116 for j := 0; j <= i-1; j++ { 117 txFrom := deps.allOutputs[j] 118 119 newDependencies = depsHelper(newDependencies, txFrom, txTo, i, j) 120 } 121 } 122 123 return newDependencies 124 } 125 126 // Find the longest execution path in the DAG 127 func (d DAG) LongestPath(stats map[int]ExecutionStat) ([]int, uint64) { 128 prev := make(map[int]int, len(d.GetVertices())) 129 130 for i := 0; i < len(d.GetVertices()); i++ { 131 prev[i] = -1 132 } 133 134 pathWeights := make(map[int]uint64, len(d.GetVertices())) 135 136 maxPath := 0 137 maxPathWeight := uint64(0) 138 139 idxToId := make(map[int]string, len(d.GetVertices())) 140 141 for k, i := range d.GetVertices() { 142 idxToId[i.(int)] = k 143 } 144 145 for i := 0; i < len(idxToId); i++ { 146 parents, _ := d.GetParents(idxToId[i]) 147 148 if len(parents) > 0 { 149 for _, p := range parents { 150 weight := pathWeights[p.(int)] + stats[i].End - stats[i].Start 151 if weight > pathWeights[i] { 152 pathWeights[i] = weight 153 prev[i] = p.(int) 154 } 155 } 156 } else { 157 pathWeights[i] = stats[i].End - stats[i].Start 158 } 159 160 if pathWeights[i] > maxPathWeight { 161 maxPath = i 162 maxPathWeight = pathWeights[i] 163 } 164 } 165 166 path := make([]int, 0) 167 for i := maxPath; i != -1; i = prev[i] { 168 path = append(path, i) 169 } 170 171 // Reverse the path so the transactions are in the ascending order 172 for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 { 173 path[i], path[j] = path[j], path[i] 174 } 175 176 return path, maxPathWeight 177 } 178 179 func (d DAG) Report(stats map[int]ExecutionStat, out func(string)) { 180 longestPath, weight := d.LongestPath(stats) 181 182 serialWeight := uint64(0) 183 184 for i := 0; i < len(d.GetVertices()); i++ { 185 serialWeight += stats[i].End - stats[i].Start 186 } 187 188 makeStrs := func(ints []int) (ret []string) { 189 for _, v := range ints { 190 ret = append(ret, fmt.Sprint(v)) 191 } 192 193 return 194 } 195 196 out("Longest execution path:") 197 out(fmt.Sprintf("(%v) %v", len(longestPath), strings.Join(makeStrs(longestPath), "->"))) 198 199 out(fmt.Sprintf("Longest path ideal execution time: %v of %v (serial total), %v%%", time.Duration(weight), 200 time.Duration(serialWeight), fmt.Sprintf("%.1f", float64(weight)*100.0/float64(serialWeight)))) 201 }