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  }