github.com/bloxroute-labs/bor@v0.1.4/les/benchmark.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "math/big" 23 "math/rand" 24 "time" 25 26 "github.com/maticnetwork/bor/common" 27 "github.com/maticnetwork/bor/common/mclock" 28 "github.com/maticnetwork/bor/core/rawdb" 29 "github.com/maticnetwork/bor/core/types" 30 "github.com/maticnetwork/bor/crypto" 31 "github.com/maticnetwork/bor/les/flowcontrol" 32 "github.com/maticnetwork/bor/log" 33 "github.com/maticnetwork/bor/p2p" 34 "github.com/maticnetwork/bor/p2p/enode" 35 "github.com/maticnetwork/bor/params" 36 "github.com/maticnetwork/bor/rlp" 37 ) 38 39 // requestBenchmark is an interface for different randomized request generators 40 type requestBenchmark interface { 41 // init initializes the generator for generating the given number of randomized requests 42 init(pm *ProtocolManager, count int) error 43 // request initiates sending a single request to the given peer 44 request(peer *peer, index int) error 45 } 46 47 // benchmarkBlockHeaders implements requestBenchmark 48 type benchmarkBlockHeaders struct { 49 amount, skip int 50 reverse, byHash bool 51 offset, randMax int64 52 hashes []common.Hash 53 } 54 55 func (b *benchmarkBlockHeaders) init(pm *ProtocolManager, count int) error { 56 d := int64(b.amount-1) * int64(b.skip+1) 57 b.offset = 0 58 b.randMax = pm.blockchain.CurrentHeader().Number.Int64() + 1 - d 59 if b.randMax < 0 { 60 return fmt.Errorf("chain is too short") 61 } 62 if b.reverse { 63 b.offset = d 64 } 65 if b.byHash { 66 b.hashes = make([]common.Hash, count) 67 for i := range b.hashes { 68 b.hashes[i] = rawdb.ReadCanonicalHash(pm.chainDb, uint64(b.offset+rand.Int63n(b.randMax))) 69 } 70 } 71 return nil 72 } 73 74 func (b *benchmarkBlockHeaders) request(peer *peer, index int) error { 75 if b.byHash { 76 return peer.RequestHeadersByHash(0, 0, b.hashes[index], b.amount, b.skip, b.reverse) 77 } else { 78 return peer.RequestHeadersByNumber(0, 0, uint64(b.offset+rand.Int63n(b.randMax)), b.amount, b.skip, b.reverse) 79 } 80 } 81 82 // benchmarkBodiesOrReceipts implements requestBenchmark 83 type benchmarkBodiesOrReceipts struct { 84 receipts bool 85 hashes []common.Hash 86 } 87 88 func (b *benchmarkBodiesOrReceipts) init(pm *ProtocolManager, count int) error { 89 randMax := pm.blockchain.CurrentHeader().Number.Int64() + 1 90 b.hashes = make([]common.Hash, count) 91 for i := range b.hashes { 92 b.hashes[i] = rawdb.ReadCanonicalHash(pm.chainDb, uint64(rand.Int63n(randMax))) 93 } 94 return nil 95 } 96 97 func (b *benchmarkBodiesOrReceipts) request(peer *peer, index int) error { 98 if b.receipts { 99 return peer.RequestReceipts(0, 0, []common.Hash{b.hashes[index]}) 100 } else { 101 return peer.RequestBodies(0, 0, []common.Hash{b.hashes[index]}) 102 } 103 } 104 105 // benchmarkProofsOrCode implements requestBenchmark 106 type benchmarkProofsOrCode struct { 107 code bool 108 headHash common.Hash 109 } 110 111 func (b *benchmarkProofsOrCode) init(pm *ProtocolManager, count int) error { 112 b.headHash = pm.blockchain.CurrentHeader().Hash() 113 return nil 114 } 115 116 func (b *benchmarkProofsOrCode) request(peer *peer, index int) error { 117 key := make([]byte, 32) 118 rand.Read(key) 119 if b.code { 120 return peer.RequestCode(0, 0, []CodeReq{{BHash: b.headHash, AccKey: key}}) 121 } else { 122 return peer.RequestProofs(0, 0, []ProofReq{{BHash: b.headHash, Key: key}}) 123 } 124 } 125 126 // benchmarkHelperTrie implements requestBenchmark 127 type benchmarkHelperTrie struct { 128 bloom bool 129 reqCount int 130 sectionCount, headNum uint64 131 } 132 133 func (b *benchmarkHelperTrie) init(pm *ProtocolManager, count int) error { 134 if b.bloom { 135 b.sectionCount, b.headNum, _ = pm.server.bloomTrieIndexer.Sections() 136 } else { 137 b.sectionCount, _, _ = pm.server.chtIndexer.Sections() 138 b.headNum = b.sectionCount*params.CHTFrequency - 1 139 } 140 if b.sectionCount == 0 { 141 return fmt.Errorf("no processed sections available") 142 } 143 return nil 144 } 145 146 func (b *benchmarkHelperTrie) request(peer *peer, index int) error { 147 reqs := make([]HelperTrieReq, b.reqCount) 148 149 if b.bloom { 150 bitIdx := uint16(rand.Intn(2048)) 151 for i := range reqs { 152 key := make([]byte, 10) 153 binary.BigEndian.PutUint16(key[:2], bitIdx) 154 binary.BigEndian.PutUint64(key[2:], uint64(rand.Int63n(int64(b.sectionCount)))) 155 reqs[i] = HelperTrieReq{Type: htBloomBits, TrieIdx: b.sectionCount - 1, Key: key} 156 } 157 } else { 158 for i := range reqs { 159 key := make([]byte, 8) 160 binary.BigEndian.PutUint64(key[:], uint64(rand.Int63n(int64(b.headNum)))) 161 reqs[i] = HelperTrieReq{Type: htCanonical, TrieIdx: b.sectionCount - 1, Key: key, AuxReq: auxHeader} 162 } 163 } 164 165 return peer.RequestHelperTrieProofs(0, 0, reqs) 166 } 167 168 // benchmarkTxSend implements requestBenchmark 169 type benchmarkTxSend struct { 170 txs types.Transactions 171 } 172 173 func (b *benchmarkTxSend) init(pm *ProtocolManager, count int) error { 174 key, _ := crypto.GenerateKey() 175 addr := crypto.PubkeyToAddress(key.PublicKey) 176 signer := types.NewEIP155Signer(big.NewInt(18)) 177 b.txs = make(types.Transactions, count) 178 179 for i := range b.txs { 180 data := make([]byte, txSizeCostLimit) 181 rand.Read(data) 182 tx, err := types.SignTx(types.NewTransaction(0, addr, new(big.Int), 0, new(big.Int), data), signer, key) 183 if err != nil { 184 panic(err) 185 } 186 b.txs[i] = tx 187 } 188 return nil 189 } 190 191 func (b *benchmarkTxSend) request(peer *peer, index int) error { 192 enc, _ := rlp.EncodeToBytes(types.Transactions{b.txs[index]}) 193 return peer.SendTxs(0, 0, enc) 194 } 195 196 // benchmarkTxStatus implements requestBenchmark 197 type benchmarkTxStatus struct{} 198 199 func (b *benchmarkTxStatus) init(pm *ProtocolManager, count int) error { 200 return nil 201 } 202 203 func (b *benchmarkTxStatus) request(peer *peer, index int) error { 204 var hash common.Hash 205 rand.Read(hash[:]) 206 return peer.RequestTxStatus(0, 0, []common.Hash{hash}) 207 } 208 209 // benchmarkSetup stores measurement data for a single benchmark type 210 type benchmarkSetup struct { 211 req requestBenchmark 212 totalCount int 213 totalTime, avgTime time.Duration 214 maxInSize, maxOutSize uint32 215 err error 216 } 217 218 // runBenchmark runs a benchmark cycle for all benchmark types in the specified 219 // number of passes 220 func (pm *ProtocolManager) runBenchmark(benchmarks []requestBenchmark, passCount int, targetTime time.Duration) []*benchmarkSetup { 221 setup := make([]*benchmarkSetup, len(benchmarks)) 222 for i, b := range benchmarks { 223 setup[i] = &benchmarkSetup{req: b} 224 } 225 for i := 0; i < passCount; i++ { 226 log.Info("Running benchmark", "pass", i+1, "total", passCount) 227 todo := make([]*benchmarkSetup, len(benchmarks)) 228 copy(todo, setup) 229 for len(todo) > 0 { 230 // select a random element 231 index := rand.Intn(len(todo)) 232 next := todo[index] 233 todo[index] = todo[len(todo)-1] 234 todo = todo[:len(todo)-1] 235 236 if next.err == nil { 237 // calculate request count 238 count := 50 239 if next.totalTime > 0 { 240 count = int(uint64(next.totalCount) * uint64(targetTime) / uint64(next.totalTime)) 241 } 242 if err := pm.measure(next, count); err != nil { 243 next.err = err 244 } 245 } 246 } 247 } 248 log.Info("Benchmark completed") 249 250 for _, s := range setup { 251 if s.err == nil { 252 s.avgTime = s.totalTime / time.Duration(s.totalCount) 253 } 254 } 255 return setup 256 } 257 258 // meteredPipe implements p2p.MsgReadWriter and remembers the largest single 259 // message size sent through the pipe 260 type meteredPipe struct { 261 rw p2p.MsgReadWriter 262 maxSize uint32 263 } 264 265 func (m *meteredPipe) ReadMsg() (p2p.Msg, error) { 266 return m.rw.ReadMsg() 267 } 268 269 func (m *meteredPipe) WriteMsg(msg p2p.Msg) error { 270 if msg.Size > m.maxSize { 271 m.maxSize = msg.Size 272 } 273 return m.rw.WriteMsg(msg) 274 } 275 276 // measure runs a benchmark for a single type in a single pass, with the given 277 // number of requests 278 func (pm *ProtocolManager) measure(setup *benchmarkSetup, count int) error { 279 clientPipe, serverPipe := p2p.MsgPipe() 280 clientMeteredPipe := &meteredPipe{rw: clientPipe} 281 serverMeteredPipe := &meteredPipe{rw: serverPipe} 282 var id enode.ID 283 rand.Read(id[:]) 284 clientPeer := pm.newPeer(lpv2, NetworkId, p2p.NewPeer(id, "client", nil), clientMeteredPipe) 285 serverPeer := pm.newPeer(lpv2, NetworkId, p2p.NewPeer(id, "server", nil), serverMeteredPipe) 286 serverPeer.sendQueue = newExecQueue(count) 287 serverPeer.announceType = announceTypeNone 288 serverPeer.fcCosts = make(requestCostTable) 289 c := &requestCosts{} 290 for code := range requests { 291 serverPeer.fcCosts[code] = c 292 } 293 serverPeer.fcParams = flowcontrol.ServerParams{BufLimit: 1, MinRecharge: 1} 294 serverPeer.fcClient = flowcontrol.NewClientNode(pm.server.fcManager, serverPeer.fcParams) 295 defer serverPeer.fcClient.Disconnect() 296 297 if err := setup.req.init(pm, count); err != nil { 298 return err 299 } 300 301 errCh := make(chan error, 10) 302 start := mclock.Now() 303 304 go func() { 305 for i := 0; i < count; i++ { 306 if err := setup.req.request(clientPeer, i); err != nil { 307 errCh <- err 308 return 309 } 310 } 311 }() 312 go func() { 313 for i := 0; i < count; i++ { 314 if err := pm.handleMsg(serverPeer); err != nil { 315 errCh <- err 316 return 317 } 318 } 319 }() 320 go func() { 321 for i := 0; i < count; i++ { 322 msg, err := clientPipe.ReadMsg() 323 if err != nil { 324 errCh <- err 325 return 326 } 327 var i interface{} 328 msg.Decode(&i) 329 } 330 // at this point we can be sure that the other two 331 // goroutines finished successfully too 332 close(errCh) 333 }() 334 select { 335 case err := <-errCh: 336 if err != nil { 337 return err 338 } 339 case <-pm.quitSync: 340 clientPipe.Close() 341 serverPipe.Close() 342 return fmt.Errorf("Benchmark cancelled") 343 } 344 345 setup.totalTime += time.Duration(mclock.Now() - start) 346 setup.totalCount += count 347 setup.maxInSize = clientMeteredPipe.maxSize 348 setup.maxOutSize = serverMeteredPipe.maxSize 349 clientPipe.Close() 350 serverPipe.Close() 351 return nil 352 }