github.com/aquanetwork/aquachain@v1.7.8/core/bench_test.go (about) 1 // Copyright 2015 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "crypto/ecdsa" 21 "io/ioutil" 22 "math/big" 23 "os" 24 "testing" 25 26 "gitlab.com/aquachain/aquachain/aquadb" 27 "gitlab.com/aquachain/aquachain/common" 28 "gitlab.com/aquachain/aquachain/common/math" 29 "gitlab.com/aquachain/aquachain/consensus/aquahash" 30 "gitlab.com/aquachain/aquachain/core/types" 31 "gitlab.com/aquachain/aquachain/core/vm" 32 "gitlab.com/aquachain/aquachain/crypto" 33 "gitlab.com/aquachain/aquachain/params" 34 ) 35 36 func BenchmarkInsertChain_empty_memdb(b *testing.B) { 37 benchInsertChain(b, false, nil) 38 } 39 func BenchmarkInsertChain_empty_diskdb(b *testing.B) { 40 benchInsertChain(b, true, nil) 41 } 42 func BenchmarkInsertChain_valueTx_memdb(b *testing.B) { 43 benchInsertChain(b, false, genValueTx(0)) 44 } 45 func BenchmarkInsertChain_valueTx_diskdb(b *testing.B) { 46 benchInsertChain(b, true, genValueTx(0)) 47 } 48 func BenchmarkInsertChain_valueTx_100kB_memdb(b *testing.B) { 49 benchInsertChain(b, false, genValueTx(100*1024)) 50 } 51 func BenchmarkInsertChain_valueTx_100kB_diskdb(b *testing.B) { 52 benchInsertChain(b, true, genValueTx(100*1024)) 53 } 54 func BenchmarkInsertChain_uncles_memdb(b *testing.B) { 55 benchInsertChain(b, false, genUncles) 56 } 57 func BenchmarkInsertChain_uncles_diskdb(b *testing.B) { 58 benchInsertChain(b, true, genUncles) 59 } 60 func BenchmarkInsertChain_ring200_memdb(b *testing.B) { 61 benchInsertChain(b, false, genTxRing(200)) 62 } 63 func BenchmarkInsertChain_ring200_diskdb(b *testing.B) { 64 benchInsertChain(b, true, genTxRing(200)) 65 } 66 func BenchmarkInsertChain_ring1000_memdb(b *testing.B) { 67 benchInsertChain(b, false, genTxRing(1000)) 68 } 69 func BenchmarkInsertChain_ring1000_diskdb(b *testing.B) { 70 benchInsertChain(b, true, genTxRing(1000)) 71 } 72 73 var ( 74 // This is the content of the genesis block used by the benchmarks. 75 benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 76 benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey) 77 benchRootFunds = math.BigPow(2, 100) 78 ) 79 80 // genValueTx returns a block generator that includes a single 81 // value-transfer transaction with n bytes of extra data in each 82 // block. 83 func genValueTx(nbytes int) func(int, *BlockGen) { 84 return func(i int, gen *BlockGen) { 85 toaddr := common.Address{} 86 data := make([]byte, nbytes) 87 gas, _ := IntrinsicGas(data, false, false) 88 tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) 89 gen.AddTx(tx) 90 } 91 } 92 93 var ( 94 ringKeys = make([]*ecdsa.PrivateKey, 1000) 95 ringAddrs = make([]common.Address, len(ringKeys)) 96 ) 97 98 func init() { 99 ringKeys[0] = benchRootKey 100 ringAddrs[0] = benchRootAddr 101 for i := 1; i < len(ringKeys); i++ { 102 ringKeys[i], _ = crypto.GenerateKey() 103 ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey) 104 } 105 } 106 107 // genTxRing returns a block generator that sends ether in a ring 108 // among n accounts. This is creates n entries in the state database 109 // and fills the blocks with many small transactions. 110 func genTxRing(naccounts int) func(int, *BlockGen) { 111 from := 0 112 return func(i int, gen *BlockGen) { 113 gas := CalcGasLimit(gen.PrevBlock(i - 1)) 114 for { 115 gas -= params.TxGas 116 if gas < params.TxGas { 117 break 118 } 119 to := (from + 1) % naccounts 120 tx := types.NewTransaction( 121 gen.TxNonce(ringAddrs[from]), 122 ringAddrs[to], 123 benchRootFunds, 124 params.TxGas, 125 nil, 126 nil, 127 ) 128 tx, _ = types.SignTx(tx, types.HomesteadSigner{}, ringKeys[from]) 129 gen.AddTx(tx) 130 from = to 131 } 132 } 133 } 134 135 // genUncles generates blocks with two uncle headers. 136 func genUncles(i int, gen *BlockGen) { 137 if i >= 6 { 138 b2 := gen.PrevBlock(i - 6).Header() 139 b2.Extra = []byte("foo") 140 gen.AddUncle(b2) 141 b3 := gen.PrevBlock(i - 6).Header() 142 b3.Extra = []byte("bar") 143 gen.AddUncle(b3) 144 } 145 } 146 147 func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { 148 // Create the database in memory or in a temporary directory. 149 var db aquadb.Database 150 if !disk { 151 db = aquadb.NewMemDatabase() 152 } else { 153 dir, err := ioutil.TempDir("", "aqua-core-bench") 154 if err != nil { 155 b.Fatalf("cannot create temporary directory: %v", err) 156 } 157 defer os.RemoveAll(dir) 158 db, err = aquadb.NewLDBDatabase(dir, 128, 128) 159 if err != nil { 160 b.Fatalf("cannot create temporary database: %v", err) 161 } 162 defer db.Close() 163 } 164 165 // Generate a chain of b.N blocks using the supplied block 166 // generator function. 167 gspec := Genesis{ 168 Config: params.TestChainConfig, 169 Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}}, 170 } 171 genesis := gspec.MustCommit(db) 172 chain, _ := GenerateChain(gspec.Config, genesis, aquahash.NewFaker(), db, b.N, gen) 173 174 // Time the insertion of the new chain. 175 // State and blocks are stored in the same DB. 176 chainman, _ := NewBlockChain(db, nil, gspec.Config, aquahash.NewFaker(), vm.Config{}) 177 defer chainman.Stop() 178 b.ReportAllocs() 179 b.ResetTimer() 180 if i, err := chainman.InsertChain(chain); err != nil { 181 b.Fatalf("insert error (block %d): %v\n", i, err) 182 } 183 } 184 185 func BenchmarkChainRead_header_10k(b *testing.B) { 186 benchReadChain(b, false, 10000) 187 } 188 func BenchmarkChainRead_full_10k(b *testing.B) { 189 benchReadChain(b, true, 10000) 190 } 191 func BenchmarkChainRead_header_100k(b *testing.B) { 192 benchReadChain(b, false, 100000) 193 } 194 func BenchmarkChainRead_full_100k(b *testing.B) { 195 benchReadChain(b, true, 100000) 196 } 197 func BenchmarkChainRead_header_500k(b *testing.B) { 198 benchReadChain(b, false, 500000) 199 } 200 func BenchmarkChainRead_full_500k(b *testing.B) { 201 benchReadChain(b, true, 500000) 202 } 203 func BenchmarkChainWrite_header_10k(b *testing.B) { 204 benchWriteChain(b, false, 10000) 205 } 206 func BenchmarkChainWrite_full_10k(b *testing.B) { 207 benchWriteChain(b, true, 10000) 208 } 209 func BenchmarkChainWrite_header_100k(b *testing.B) { 210 benchWriteChain(b, false, 100000) 211 } 212 func BenchmarkChainWrite_full_100k(b *testing.B) { 213 benchWriteChain(b, true, 100000) 214 } 215 func BenchmarkChainWrite_header_500k(b *testing.B) { 216 benchWriteChain(b, false, 500000) 217 } 218 func BenchmarkChainWrite_full_500k(b *testing.B) { 219 benchWriteChain(b, true, 500000) 220 } 221 222 // makeChainForBench writes a given number of headers or empty blocks/receipts 223 // into a database. 224 func makeChainForBench(db aquadb.Database, full bool, count uint64) { 225 var hash common.Hash 226 for n := uint64(0); n < count; n++ { 227 header := &types.Header{ 228 Coinbase: common.Address{}, 229 Number: big.NewInt(int64(n)), 230 ParentHash: hash, 231 Difficulty: big.NewInt(1), 232 UncleHash: types.EmptyUncleHash, 233 TxHash: types.EmptyRootHash, 234 ReceiptHash: types.EmptyRootHash, 235 } 236 hash = header.Hash() 237 WriteHeader(db, header) 238 WriteCanonicalHash(db, hash, n) 239 WriteTd(db, hash, n, big.NewInt(int64(n+1))) 240 if full || n == 0 { 241 block := types.NewBlockWithHeader(header) 242 WriteBody(db, hash, n, block.Body()) 243 WriteBlockReceipts(db, hash, n, nil) 244 } 245 } 246 } 247 248 func benchWriteChain(b *testing.B, full bool, count uint64) { 249 for i := 0; i < b.N; i++ { 250 dir, err := ioutil.TempDir("", "aqua-chain-bench") 251 if err != nil { 252 b.Fatalf("cannot create temporary directory: %v", err) 253 } 254 db, err := aquadb.NewLDBDatabase(dir, 128, 1024) 255 if err != nil { 256 b.Fatalf("error opening database at %v: %v", dir, err) 257 } 258 makeChainForBench(db, full, count) 259 db.Close() 260 os.RemoveAll(dir) 261 } 262 } 263 264 func benchReadChain(b *testing.B, full bool, count uint64) { 265 dir, err := ioutil.TempDir("", "aqua-chain-bench") 266 if err != nil { 267 b.Fatalf("cannot create temporary directory: %v", err) 268 } 269 defer os.RemoveAll(dir) 270 271 db, err := aquadb.NewLDBDatabase(dir, 128, 1024) 272 if err != nil { 273 b.Fatalf("error opening database at %v: %v", dir, err) 274 } 275 makeChainForBench(db, full, count) 276 db.Close() 277 278 b.ReportAllocs() 279 b.ResetTimer() 280 281 for i := 0; i < b.N; i++ { 282 db, err := aquadb.NewLDBDatabase(dir, 128, 1024) 283 if err != nil { 284 b.Fatalf("error opening database at %v: %v", dir, err) 285 } 286 chain, err := NewBlockChain(db, nil, params.TestChainConfig, aquahash.NewFaker(), vm.Config{}) 287 if err != nil { 288 b.Fatalf("error creating chain: %v", err) 289 } 290 291 for n := uint64(0); n < count; n++ { 292 header := chain.GetHeaderByNumber(n) 293 if full { 294 hash := header.Hash() 295 GetBodyNoVersion(db, hash, n) 296 GetBlockReceipts(db, hash, n) 297 } 298 } 299 300 chain.Stop() 301 db.Close() 302 } 303 }