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