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