github.com/klaytn/klaytn@v1.12.1/blockchain/bench_test.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from core/bench_test.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package blockchain
    22  
    23  import (
    24  	"crypto/ecdsa"
    25  	"math/big"
    26  	"os"
    27  	"testing"
    28  
    29  	"github.com/klaytn/klaytn/blockchain/types"
    30  	"github.com/klaytn/klaytn/blockchain/vm"
    31  	"github.com/klaytn/klaytn/common"
    32  	"github.com/klaytn/klaytn/common/math"
    33  	"github.com/klaytn/klaytn/consensus/gxhash"
    34  	"github.com/klaytn/klaytn/crypto"
    35  	"github.com/klaytn/klaytn/params"
    36  	"github.com/klaytn/klaytn/storage/database"
    37  )
    38  
    39  func BenchmarkInsertChain_empty_memDB(b *testing.B) {
    40  	benchInsertChain(b, database.MemoryDB, nil)
    41  }
    42  
    43  func BenchmarkInsertChain_empty_levelDB(b *testing.B) {
    44  	benchInsertChain(b, database.LevelDB, nil)
    45  }
    46  
    47  func BenchmarkInsertChain_empty_badgerDB(b *testing.B) {
    48  	benchInsertChain(b, database.BadgerDB, nil)
    49  }
    50  
    51  func BenchmarkInsertChain_valueTx_memDB(b *testing.B) {
    52  	benchInsertChain(b, database.MemoryDB, genValueTx(0))
    53  }
    54  
    55  func BenchmarkInsertChain_valueTx_levelDB(b *testing.B) {
    56  	benchInsertChain(b, database.LevelDB, genValueTx(0))
    57  }
    58  
    59  func BenchmarkInsertChain_valueTx_badgerDB(b *testing.B) {
    60  	benchInsertChain(b, database.BadgerDB, genValueTx(0))
    61  }
    62  
    63  func BenchmarkInsertChain_valueTx_10kB_memDB(b *testing.B) {
    64  	benchInsertChain(b, database.MemoryDB, genValueTx(100*1024))
    65  }
    66  
    67  func BenchmarkInsertChain_valueTx_10kB_levelDB(b *testing.B) {
    68  	benchInsertChain(b, database.LevelDB, genValueTx(100*1024))
    69  }
    70  
    71  func BenchmarkInsertChain_valueTx_10kB_badgerDB(b *testing.B) {
    72  	benchInsertChain(b, database.BadgerDB, genValueTx(100*1024))
    73  }
    74  
    75  func BenchmarkInsertChain_ring200_memDB(b *testing.B) {
    76  	benchInsertChain(b, database.MemoryDB, genTxRing(200))
    77  }
    78  
    79  func BenchmarkInsertChain_ring200_levelDB(b *testing.B) {
    80  	benchInsertChain(b, database.LevelDB, genTxRing(200))
    81  }
    82  
    83  func BenchmarkInsertChain_ring200_badgerDB(b *testing.B) {
    84  	benchInsertChain(b, database.BadgerDB, genTxRing(200))
    85  }
    86  
    87  func BenchmarkInsertChain_ring1000_memDB(b *testing.B) {
    88  	benchInsertChain(b, database.MemoryDB, genTxRing(1000))
    89  }
    90  
    91  func BenchmarkInsertChain_ring1000_levelDB(b *testing.B) {
    92  	benchInsertChain(b, database.LevelDB, genTxRing(1000))
    93  }
    94  
    95  func BenchmarkInsertChain_ring1000_badgerDB(b *testing.B) {
    96  	benchInsertChain(b, database.BadgerDB, genTxRing(1000))
    97  }
    98  
    99  var (
   100  	// This is the content of the genesis block used by the benchmarks.
   101  	benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   102  	benchRootAddr   = crypto.PubkeyToAddress(benchRootKey.PublicKey)
   103  	benchRootFunds  = math.BigPow(2, 100)
   104  )
   105  
   106  // genValueTx returns a block generator that includes a single
   107  // value-transfer transaction with n bytes of extra data in each block.
   108  func genValueTx(nbytes int) func(int, *BlockGen) {
   109  	return func(i int, gen *BlockGen) {
   110  		toaddr := common.Address{}
   111  		data := make([]byte, nbytes)
   112  		gas, _ := types.IntrinsicGas(data, nil, false, params.TestChainConfig.Rules(big.NewInt(0)))
   113  		signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID)
   114  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), signer, benchRootKey)
   115  		gen.AddTx(tx)
   116  	}
   117  }
   118  
   119  var (
   120  	ringKeys  = make([]*ecdsa.PrivateKey, 1000)
   121  	ringAddrs = make([]common.Address, len(ringKeys))
   122  )
   123  
   124  func init() {
   125  	ringKeys[0] = benchRootKey
   126  	ringAddrs[0] = benchRootAddr
   127  	for i := 1; i < len(ringKeys); i++ {
   128  		ringKeys[i], _ = crypto.GenerateKey()
   129  		ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey)
   130  	}
   131  }
   132  
   133  // genTxRing returns a block generator that sends KLAY in a ring
   134  // among n accounts. This is creates n entries in the state database
   135  // and fills the blocks with many small transactions.
   136  func genTxRing(naccounts int) func(int, *BlockGen) {
   137  	from := 0
   138  	signer := types.LatestSignerForChainID(params.TestChainConfig.ChainID)
   139  	return func(i int, gen *BlockGen) {
   140  		gas := uint64(1000000)
   141  		for {
   142  			gas -= params.TxGas
   143  			if gas < params.TxGas {
   144  				break
   145  			}
   146  			to := (from + 1) % naccounts
   147  			tx := types.NewTransaction(
   148  				gen.TxNonce(ringAddrs[from]),
   149  				ringAddrs[to],
   150  				benchRootFunds,
   151  				params.TxGas,
   152  				nil,
   153  				nil,
   154  			)
   155  			tx, _ = types.SignTx(tx, signer, ringKeys[from])
   156  			gen.AddTx(tx)
   157  			from = to
   158  		}
   159  	}
   160  }
   161  
   162  func benchInsertChain(b *testing.B, dbType database.DBType, gen func(int, *BlockGen)) {
   163  	// 1. Create the database
   164  	dir := genTempDirForDB(b)
   165  	defer os.RemoveAll(dir)
   166  
   167  	db := genDBManagerForTest(dir, dbType)
   168  	defer db.Close()
   169  
   170  	// 2. Generate a chain of b.N blocks using the supplied block generator function.
   171  	gspec := Genesis{
   172  		Config: params.TestChainConfig,
   173  		Alloc:  GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
   174  	}
   175  	genesis := gspec.MustCommit(db)
   176  	chain, _ := GenerateChain(gspec.Config, genesis, gxhash.NewFaker(), db, b.N, gen)
   177  
   178  	// Time the insertion of the new chain.
   179  	// State and blocks are stored in the same DB.
   180  	chainman, _ := NewBlockChain(db, nil, gspec.Config, gxhash.NewFaker(), vm.Config{})
   181  	defer chainman.Stop()
   182  	b.ReportAllocs()
   183  	b.ResetTimer()
   184  	if i, err := chainman.InsertChain(chain); err != nil {
   185  		b.Fatalf("insert error (block %d): %v\n", i, err)
   186  	}
   187  }
   188  
   189  // BenchmarkChainRead Series
   190  func BenchmarkChainRead_header_10k_levelDB(b *testing.B) {
   191  	benchReadChain(b, false, database.LevelDB, 10000)
   192  }
   193  
   194  func BenchmarkChainRead_header_10k_badgerDB(b *testing.B) {
   195  	benchReadChain(b, false, database.BadgerDB, 10000)
   196  }
   197  
   198  func BenchmarkChainRead_full_10k_levelDB(b *testing.B) {
   199  	benchReadChain(b, true, database.LevelDB, 10000)
   200  }
   201  
   202  func BenchmarkChainRead_full_10k_badgerDB(b *testing.B) {
   203  	benchReadChain(b, true, database.BadgerDB, 10000)
   204  }
   205  
   206  func BenchmarkChainRead_header_100k_levelDB(b *testing.B) {
   207  	benchReadChain(b, false, database.LevelDB, 100000)
   208  }
   209  
   210  func BenchmarkChainRead_header_100k_badgerDB(b *testing.B) {
   211  	benchReadChain(b, false, database.BadgerDB, 100000)
   212  }
   213  
   214  func BenchmarkChainRead_full_100k_levelDB(b *testing.B) {
   215  	benchReadChain(b, true, database.LevelDB, 100000)
   216  }
   217  
   218  func BenchmarkChainRead_full_100k_badgerDB(b *testing.B) {
   219  	benchReadChain(b, true, database.BadgerDB, 100000)
   220  }
   221  
   222  // Disabled because of too long test time
   223  //func BenchmarkChainRead_header_500k_levelDB(b *testing.B) {
   224  //	benchReadChain(b, false, database.LevelDB,500000)
   225  //}
   226  //func BenchmarkChainRead_header_500k_badgerDB(b *testing.B) {
   227  //	benchReadChain(b, false, database.BadgerDB, 500000)
   228  //}
   229  //
   230  //func BenchmarkChainRead_full_500k_levelDB(b *testing.B) {
   231  //	benchReadChain(b, true, database.LevelDB,500000)
   232  //}
   233  //func BenchmarkChainRead_full_500k_badgerDB(b *testing.B) {
   234  //	benchReadChain(b, true, database.BadgerDB,500000)
   235  //}
   236  
   237  // BenchmarkChainWrite Series
   238  func BenchmarkChainWrite_header_10k_levelDB(b *testing.B) {
   239  	benchWriteChain(b, false, database.LevelDB, 10000)
   240  }
   241  
   242  func BenchmarkChainWrite_header_10k_badgerDB(b *testing.B) {
   243  	benchWriteChain(b, false, database.BadgerDB, 10000)
   244  }
   245  
   246  func BenchmarkChainWrite_full_10k_levelDB(b *testing.B) {
   247  	benchWriteChain(b, true, database.LevelDB, 10000)
   248  }
   249  
   250  func BenchmarkChainWrite_full_10k_badgerDB(b *testing.B) {
   251  	benchWriteChain(b, true, database.BadgerDB, 10000)
   252  }
   253  
   254  func BenchmarkChainWrite_header_100k_levelDB(b *testing.B) {
   255  	benchWriteChain(b, false, database.LevelDB, 100000)
   256  }
   257  
   258  func BenchmarkChainWrite_header_100k_badgerDB(b *testing.B) {
   259  	benchWriteChain(b, false, database.BadgerDB, 100000)
   260  }
   261  
   262  func BenchmarkChainWrite_full_100k_levelDB(b *testing.B) {
   263  	benchWriteChain(b, true, database.LevelDB, 100000)
   264  }
   265  
   266  func BenchmarkChainWrite_full_100k_badgerDB(b *testing.B) {
   267  	benchWriteChain(b, true, database.BadgerDB, 100000)
   268  }
   269  
   270  // Disabled because of too long test time
   271  //func BenchmarkChainWrite_header_500k_levelDB(b *testing.B) {
   272  //	benchWriteChain(b, false, database.LevelDB,500000)
   273  //}
   274  //func BenchmarkChainWrite_header_500k_badgerDB(b *testing.B) {
   275  //	benchWriteChain(b, false, database.BadgerDB, 500000)
   276  //}
   277  //
   278  //func BenchmarkChainWrite_full_500k_levelDB(b *testing.B) {
   279  //	benchWriteChain(b, true, database.LevelDB,500000)
   280  //}
   281  //func BenchmarkChainWrite_full_500k_badgerDB(b *testing.B) {
   282  //	benchWriteChain(b, true, database.BadgerDB,500000)
   283  //}
   284  
   285  // makeChainForBench writes a given number of headers or empty blocks/receipts
   286  // into a database.
   287  func makeChainForBench(db database.DBManager, full bool, count uint64) {
   288  	var hash common.Hash
   289  	for n := uint64(0); n < count; n++ {
   290  		num := new(big.Int).SetUint64(n)
   291  		header := &types.Header{
   292  			Number:      num,
   293  			ParentHash:  hash,
   294  			BlockScore:  big.NewInt(1),
   295  			TxHash:      types.EmptyRootHash(num),
   296  			ReceiptHash: types.EmptyRootHash(num),
   297  		}
   298  		hash = header.Hash()
   299  
   300  		db.WriteHeader(header)
   301  		db.WriteCanonicalHash(hash, n)
   302  		db.WriteTd(hash, n, big.NewInt(int64(n+1)))
   303  
   304  		if full || n == 0 {
   305  			db.WriteHeadBlockHash(hash)
   306  			block := types.NewBlockWithHeader(header)
   307  			db.WriteBody(hash, n, block.Body())
   308  			db.WriteReceipts(hash, n, nil)
   309  		}
   310  	}
   311  }
   312  
   313  // write 'count' blocks to database 'b.N' times
   314  func benchWriteChain(b *testing.B, full bool, databaseType database.DBType, count uint64) {
   315  	for i := 0; i < b.N; i++ {
   316  		dir := genTempDirForDB(b)
   317  
   318  		db := genDBManagerForTest(dir, databaseType)
   319  		makeChainForBench(db, full, count)
   320  
   321  		db.Close()
   322  		os.RemoveAll(dir)
   323  	}
   324  }
   325  
   326  // write 'count' blocks to database and then read 'count' blocks
   327  func benchReadChain(b *testing.B, full bool, databaseType database.DBType, count uint64) {
   328  	dir := genTempDirForDB(b)
   329  	defer os.RemoveAll(dir)
   330  
   331  	db := genDBManagerForTest(dir, databaseType)
   332  	makeChainForBench(db, full, count)
   333  	db.Close()
   334  
   335  	b.ReportAllocs()
   336  	b.ResetTimer()
   337  
   338  	for i := 0; i < b.N; i++ {
   339  
   340  		db = genDBManagerForTest(dir, databaseType)
   341  
   342  		chain, err := NewBlockChain(db, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{})
   343  		if err != nil {
   344  			b.Fatalf("error creating chain: %v", err)
   345  		}
   346  
   347  		for n := uint64(0); n < count; n++ {
   348  			header := chain.GetHeaderByNumber(n)
   349  			if full {
   350  				hash := header.Hash()
   351  				db.ReadBody(hash, n)
   352  				db.ReadReceipts(hash, n)
   353  			}
   354  		}
   355  		chain.Stop()
   356  		db.Close()
   357  	}
   358  }
   359  
   360  // genTempDirForDB returns temp dir for database
   361  func genTempDirForDB(b *testing.B) string {
   362  	dir, err := os.MkdirTemp("", "klay-blockchain-bench")
   363  	if err != nil {
   364  		b.Fatalf("cannot create temporary directory: %v", err)
   365  	}
   366  	return dir
   367  }
   368  
   369  // genDBManagerForTest returns database.Database according to entered databaseType
   370  func genDBManagerForTest(dir string, dbType database.DBType) database.DBManager {
   371  	if dbType == database.MemoryDB {
   372  		db := database.NewMemoryDBManager()
   373  		return db
   374  	} else {
   375  		dbc := &database.DBConfig{Dir: dir, DBType: dbType, LevelDBCacheSize: 128, OpenFilesLimit: 128}
   376  		return database.NewDBManager(dbc)
   377  	}
   378  }