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  }