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