github.com/MagHErmit/tendermint@v0.282.1/test/e2e/runner/benchmark.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "time" 8 9 e2e "github.com/MagHErmit/tendermint/test/e2e/pkg" 10 "github.com/MagHErmit/tendermint/types" 11 ) 12 13 // Benchmark is a simple function for fetching, calculating and printing 14 // the following metrics: 15 // 1. Average block production time 16 // 2. Block interval standard deviation 17 // 3. Max block interval (slowest block) 18 // 4. Min block interval (fastest block) 19 // 20 // Metrics are based of the `benchmarkLength`, the amount of consecutive blocks 21 // sampled from in the testnet 22 func Benchmark(testnet *e2e.Testnet, benchmarkLength int64) error { 23 block, _, err := waitForHeight(testnet, 0) 24 if err != nil { 25 return err 26 } 27 28 logger.Info("Beginning benchmark period...", "height", block.Height) 29 30 // wait for the length of the benchmark period in blocks to pass. We allow 5 seconds for each block 31 // which should be sufficient. 32 waitingTime := time.Duration(benchmarkLength*5) * time.Second 33 endHeight, err := waitForAllNodes(testnet, block.Height+benchmarkLength, waitingTime) 34 if err != nil { 35 return err 36 } 37 38 logger.Info("Ending benchmark period", "height", endHeight) 39 40 // fetch a sample of blocks 41 blocks, err := fetchBlockChainSample(testnet, benchmarkLength) 42 if err != nil { 43 return err 44 } 45 46 // slice into time intervals and collate data 47 timeIntervals := splitIntoBlockIntervals(blocks) 48 testnetStats := extractTestnetStats(timeIntervals) 49 testnetStats.startHeight = blocks[0].Header.Height 50 testnetStats.endHeight = blocks[len(blocks)-1].Header.Height 51 52 // print and return 53 logger.Info(testnetStats.String()) 54 return nil 55 } 56 57 type testnetStats struct { 58 startHeight int64 59 endHeight int64 60 61 // average time to produce a block 62 mean time.Duration 63 // standard deviation of block production 64 std float64 65 // longest time to produce a block 66 max time.Duration 67 // shortest time to produce a block 68 min time.Duration 69 } 70 71 func (t *testnetStats) String() string { 72 return fmt.Sprintf(`Benchmarked from height %v to %v 73 Mean Block Interval: %v 74 Standard Deviation: %f 75 Max Block Interval: %v 76 Min Block Interval: %v 77 `, 78 t.startHeight, 79 t.endHeight, 80 t.mean, 81 t.std, 82 t.max, 83 t.min, 84 ) 85 } 86 87 // fetchBlockChainSample waits for `benchmarkLength` amount of blocks to pass, fetching 88 // all of the headers for these blocks from an archive node and returning it. 89 func fetchBlockChainSample(testnet *e2e.Testnet, benchmarkLength int64) ([]*types.BlockMeta, error) { 90 var blocks []*types.BlockMeta 91 92 // Find the first archive node 93 archiveNode := testnet.ArchiveNodes()[0] 94 c, err := archiveNode.Client() 95 if err != nil { 96 return nil, err 97 } 98 99 // find the latest height 100 ctx := context.Background() 101 s, err := c.Status(ctx) 102 if err != nil { 103 return nil, err 104 } 105 106 to := s.SyncInfo.LatestBlockHeight 107 from := to - benchmarkLength + 1 108 if from <= testnet.InitialHeight { 109 return nil, fmt.Errorf("tesnet was unable to reach required height for benchmarking (latest height %d)", to) 110 } 111 112 // Fetch blocks 113 for from < to { 114 // fetch the blockchain metas. Currently we can only fetch 20 at a time 115 resp, err := c.BlockchainInfo(ctx, from, min(from+19, to)) 116 if err != nil { 117 return nil, err 118 } 119 120 blockMetas := resp.BlockMetas 121 // we receive blocks in descending order so we have to add them in reverse 122 for i := len(blockMetas) - 1; i >= 0; i-- { 123 if blockMetas[i].Header.Height != from { 124 return nil, fmt.Errorf("node gave us another header. Wanted %d, got %d", 125 from, 126 blockMetas[i].Header.Height, 127 ) 128 } 129 from++ 130 blocks = append(blocks, blockMetas[i]) 131 } 132 } 133 134 return blocks, nil 135 } 136 137 func splitIntoBlockIntervals(blocks []*types.BlockMeta) []time.Duration { 138 intervals := make([]time.Duration, len(blocks)-1) 139 lastTime := blocks[0].Header.Time 140 for i, block := range blocks { 141 // skip the first block 142 if i == 0 { 143 continue 144 } 145 146 intervals[i-1] = block.Header.Time.Sub(lastTime) 147 lastTime = block.Header.Time 148 } 149 return intervals 150 } 151 152 func extractTestnetStats(intervals []time.Duration) testnetStats { 153 var ( 154 sum, mean time.Duration 155 std float64 156 max = intervals[0] 157 min = intervals[0] 158 ) 159 160 for _, interval := range intervals { 161 sum += interval 162 163 if interval > max { 164 max = interval 165 } 166 167 if interval < min { 168 min = interval 169 } 170 } 171 mean = sum / time.Duration(len(intervals)) 172 173 for _, interval := range intervals { 174 diff := (interval - mean).Seconds() 175 std += math.Pow(diff, 2) 176 } 177 std = math.Sqrt(std / float64(len(intervals))) 178 179 return testnetStats{ 180 mean: mean, 181 std: std, 182 max: max, 183 min: min, 184 } 185 } 186 187 func min(a, b int64) int64 { 188 if a > b { 189 return b 190 } 191 return a 192 }