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 }