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 }