github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/core/bench_test.go (about) 1 // Copyright 2015 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum 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 "github.com/SmartMeshFoundation/Spectrum/common" 27 "github.com/SmartMeshFoundation/Spectrum/common/math" 28 "github.com/SmartMeshFoundation/Spectrum/consensus/ethash" 29 "github.com/SmartMeshFoundation/Spectrum/core/types" 30 "github.com/SmartMeshFoundation/Spectrum/core/vm" 31 "github.com/SmartMeshFoundation/Spectrum/crypto" 32 "github.com/SmartMeshFoundation/Spectrum/ethdb" 33 "github.com/SmartMeshFoundation/Spectrum/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 bigTxGas = new(big.Int).SetUint64(params.TxGas) 97 ) 98 99 func init() { 100 ringKeys[0] = benchRootKey 101 ringAddrs[0] = benchRootAddr 102 for i := 1; i < len(ringKeys); i++ { 103 ringKeys[i], _ = crypto.GenerateKey() 104 ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey) 105 } 106 } 107 108 // genTxRing returns a block generator that sends ether in a ring 109 // among n accounts. This is creates n entries in the state database 110 // and fills the blocks with many small transactions. 111 func genTxRing(naccounts int) func(int, *BlockGen) { 112 from := 0 113 return func(i int, gen *BlockGen) { 114 gas := CalcGasLimit(gen.PrevBlock(i - 1)) 115 for { 116 gas.Sub(gas, bigTxGas) 117 if gas.Cmp(bigTxGas) < 0 { 118 break 119 } 120 to := (from + 1) % naccounts 121 tx := types.NewTransaction( 122 gen.TxNonce(ringAddrs[from]), 123 ringAddrs[to], 124 benchRootFunds, 125 bigTxGas, 126 nil, 127 nil, 128 ) 129 tx, _ = types.SignTx(tx, types.HomesteadSigner{}, ringKeys[from]) 130 gen.AddTx(tx) 131 from = to 132 } 133 } 134 } 135 136 // genUncles generates blocks with two uncle headers. 137 func genUncles(i int, gen *BlockGen) { 138 if i >= 6 { 139 b2 := gen.PrevBlock(i - 6).Header() 140 b2.Extra = []byte("foo") 141 gen.AddUncle(b2) 142 b3 := gen.PrevBlock(i - 6).Header() 143 b3.Extra = []byte("bar") 144 gen.AddUncle(b3) 145 } 146 } 147 148 func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { 149 // Create the database in memory or in a temporary directory. 150 var db ethdb.Database 151 if !disk { 152 db, _ = ethdb.NewMemDatabase() 153 } else { 154 dir, err := ioutil.TempDir("", "eth-core-bench") 155 if err != nil { 156 b.Fatalf("cannot create temporary directory: %v", err) 157 } 158 defer os.RemoveAll(dir) 159 db, err = ethdb.NewLDBDatabase(dir, 128, 128) 160 if err != nil { 161 b.Fatalf("cannot create temporary database: %v", err) 162 } 163 defer db.Close() 164 } 165 166 // Generate a chain of b.N blocks using the supplied block 167 // generator function. 168 gspec := Genesis{ 169 Config: params.TestChainConfig, 170 Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}}, 171 } 172 genesis := gspec.MustCommit(db) 173 chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, b.N, gen) 174 175 // Time the insertion of the new chain. 176 // State and blocks are stored in the same DB. 177 chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) 178 defer chainman.Stop() 179 b.ReportAllocs() 180 b.ResetTimer() 181 if i, err := chainman.InsertChain(chain); err != nil { 182 b.Fatalf("insert error (block %d): %v\n", i, err) 183 } 184 } 185 186 func BenchmarkChainRead_header_10k(b *testing.B) { 187 benchReadChain(b, false, 10000) 188 } 189 func BenchmarkChainRead_full_10k(b *testing.B) { 190 benchReadChain(b, true, 10000) 191 } 192 func BenchmarkChainRead_header_100k(b *testing.B) { 193 benchReadChain(b, false, 100000) 194 } 195 func BenchmarkChainRead_full_100k(b *testing.B) { 196 benchReadChain(b, true, 100000) 197 } 198 func BenchmarkChainRead_header_500k(b *testing.B) { 199 benchReadChain(b, false, 500000) 200 } 201 func BenchmarkChainRead_full_500k(b *testing.B) { 202 benchReadChain(b, true, 500000) 203 } 204 func BenchmarkChainWrite_header_10k(b *testing.B) { 205 benchWriteChain(b, false, 10000) 206 } 207 func BenchmarkChainWrite_full_10k(b *testing.B) { 208 benchWriteChain(b, true, 10000) 209 } 210 func BenchmarkChainWrite_header_100k(b *testing.B) { 211 benchWriteChain(b, false, 100000) 212 } 213 func BenchmarkChainWrite_full_100k(b *testing.B) { 214 benchWriteChain(b, true, 100000) 215 } 216 func BenchmarkChainWrite_header_500k(b *testing.B) { 217 benchWriteChain(b, false, 500000) 218 } 219 func BenchmarkChainWrite_full_500k(b *testing.B) { 220 benchWriteChain(b, true, 500000) 221 } 222 223 // makeChainForBench writes a given number of headers or empty blocks/receipts 224 // into a database. 225 func makeChainForBench(db ethdb.Database, full bool, count uint64) { 226 var hash common.Hash 227 for n := uint64(0); n < count; n++ { 228 header := &types.Header{ 229 Coinbase: common.Address{}, 230 Number: big.NewInt(int64(n)), 231 ParentHash: hash, 232 Difficulty: big.NewInt(1), 233 UncleHash: types.EmptyUncleHash, 234 TxHash: types.EmptyRootHash, 235 ReceiptHash: types.EmptyRootHash, 236 } 237 hash = header.Hash() 238 WriteHeader(db, header) 239 WriteCanonicalHash(db, hash, n) 240 WriteTd(db, hash, n, big.NewInt(int64(n+1))) 241 if full || n == 0 { 242 block := types.NewBlockWithHeader(header) 243 WriteBody(db, hash, n, block.Body()) 244 WriteBlockReceipts(db, hash, n, nil) 245 } 246 } 247 } 248 249 func benchWriteChain(b *testing.B, full bool, count uint64) { 250 for i := 0; i < b.N; i++ { 251 dir, err := ioutil.TempDir("", "eth-chain-bench") 252 if err != nil { 253 b.Fatalf("cannot create temporary directory: %v", err) 254 } 255 db, err := ethdb.NewLDBDatabase(dir, 128, 1024) 256 if err != nil { 257 b.Fatalf("error opening database at %v: %v", dir, err) 258 } 259 makeChainForBench(db, full, count) 260 db.Close() 261 os.RemoveAll(dir) 262 } 263 } 264 265 func benchReadChain(b *testing.B, full bool, count uint64) { 266 dir, err := ioutil.TempDir("", "eth-chain-bench") 267 if err != nil { 268 b.Fatalf("cannot create temporary directory: %v", err) 269 } 270 defer os.RemoveAll(dir) 271 272 db, err := ethdb.NewLDBDatabase(dir, 128, 1024) 273 if err != nil { 274 b.Fatalf("error opening database at %v: %v", dir, err) 275 } 276 makeChainForBench(db, full, count) 277 db.Close() 278 279 b.ReportAllocs() 280 b.ResetTimer() 281 282 for i := 0; i < b.N; i++ { 283 db, err := ethdb.NewLDBDatabase(dir, 128, 1024) 284 if err != nil { 285 b.Fatalf("error opening database at %v: %v", dir, err) 286 } 287 chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) 288 if err != nil { 289 b.Fatalf("error creating chain: %v", err) 290 } 291 292 for n := uint64(0); n < count; n++ { 293 header := chain.GetHeaderByNumber(n) 294 if full { 295 hash := header.Hash() 296 GetBody(db, hash, n) 297 GetBlockReceipts(db, hash, n) 298 } 299 } 300 301 chain.Stop() 302 db.Close() 303 } 304 }