package core import ( "crypto/ecdsa" "io/ioutil" "math/big" "os" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" ) func BenchmarkInsertChain_empty_memdb(b *testing.B) { benchInsertChain(b, false, nil) } func BenchmarkInsertChain_empty_diskdb(b *testing.B) { benchInsertChain(b, true, nil) } func BenchmarkInsertChain_valueTx_memdb(b *testing.B) { benchInsertChain(b, false, genValueTx(0)) } func BenchmarkInsertChain_valueTx_diskdb(b *testing.B) { benchInsertChain(b, true, genValueTx(0)) } func BenchmarkInsertChain_valueTx_100kB_memdb(b *testing.B) { benchInsertChain(b, false, genValueTx(100*1024)) } func BenchmarkInsertChain_valueTx_100kB_diskdb(b *testing.B) { benchInsertChain(b, true, genValueTx(100*1024)) } func BenchmarkInsertChain_uncles_memdb(b *testing.B) { benchInsertChain(b, false, genUncles) } func BenchmarkInsertChain_uncles_diskdb(b *testing.B) { benchInsertChain(b, true, genUncles) } func BenchmarkInsertChain_ring200_memdb(b *testing.B) { benchInsertChain(b, false, genTxRing(200)) } func BenchmarkInsertChain_ring200_diskdb(b *testing.B) { benchInsertChain(b, true, genTxRing(200)) } func BenchmarkInsertChain_ring1000_memdb(b *testing.B) { benchInsertChain(b, false, genTxRing(1000)) } func BenchmarkInsertChain_ring1000_diskdb(b *testing.B) { benchInsertChain(b, true, genTxRing(1000)) } var ( // This is the content of the genesis block used by the benchmarks. benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey) benchRootFunds = common.BigPow(2, 100) ) // genValueTx returns a block generator that includes a single // value-transfer transaction with n bytes of extra data in each // block. func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) gas := IntrinsicGas(data) tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(benchRootKey) gen.AddTx(tx) } } var ( ringKeys = make([]*ecdsa.PrivateKey, 1000) ringAddrs = make([]common.Address, len(ringKeys)) ) func init() { ringKeys[0] = benchRootKey ringAddrs[0] = benchRootAddr for i := 1; i < len(ringKeys); i++ { ringKeys[i], _ = crypto.GenerateKey() ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey) } } // genTxRing returns a block generator that sends ether in a ring // among n accounts. This is creates n entries in the state database // and fills the blocks with many small transactions. func genTxRing(naccounts int) func(int, *BlockGen) { from := 0 return func(i int, gen *BlockGen) { gas := CalcGasLimit(gen.PrevBlock(i - 1)) for { gas.Sub(gas, params.TxGas) if gas.Cmp(params.TxGas) < 0 { break } to := (from + 1) % naccounts tx := types.NewTransaction( gen.TxNonce(ringAddrs[from]), ringAddrs[to], benchRootFunds, params.TxGas, nil, nil, ) tx, _ = tx.SignECDSA(ringKeys[from]) gen.AddTx(tx) from = to } } } // genUncles generates blocks with two uncle headers. func genUncles(i int, gen *BlockGen) { if i >= 6 { b2 := gen.PrevBlock(i - 6).Header() b2.Extra = []byte("foo") gen.AddUncle(b2) b3 := gen.PrevBlock(i - 6).Header() b3.Extra = []byte("bar") gen.AddUncle(b3) } } func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Create the database in memory or in a temporary directory. var db common.Database if !disk { db, _ = ethdb.NewMemDatabase() } else { dir, err := ioutil.TempDir("", "eth-core-bench") if err != nil { b.Fatalf("cannot create temporary directory: %v", err) } defer os.RemoveAll(dir) db, err = ethdb.NewLDBDatabase(dir) if err != nil { b.Fatalf("cannot create temporary database: %v", err) } defer db.Close() } // Generate a chain of b.N blocks using the supplied block // generator function. genesis := GenesisBlockForTesting(db, benchRootAddr, benchRootFunds) chain := GenerateChain(genesis, db, b.N, gen) // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) chainman, _ := NewChainManager(genesis, db, db, db, FakePow{}, evmux) chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux)) defer chainman.Stop() b.ReportAllocs() b.ResetTimer() if i, err := chainman.InsertChain(chain); err != nil { b.Fatalf("insert error (block %d): %v\n", i, err) } }