github.com/klaytn/klaytn@v1.12.1/tests/pregenerated_data_execution_test.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn 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 klaytn 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 klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package tests
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"runtime/pprof"
    23  	"testing"
    24  
    25  	"github.com/klaytn/klaytn/blockchain"
    26  	"github.com/klaytn/klaytn/blockchain/types"
    27  	"github.com/klaytn/klaytn/common"
    28  )
    29  
    30  const txPoolSize = 32768
    31  
    32  // BenchmarkDataExecution_Aspen generates the data with Aspen network's database configurations.
    33  func BenchmarkDataExecution_Aspen(b *testing.B) {
    34  	tc := getExecutionTestDefaultTC()
    35  	tc.testName = "BenchmarkDataExecution_Aspen"
    36  	tc.originalDataDir = aspen500_orig
    37  	tc.dbc, tc.levelDBOption = genAspenOptions()
    38  
    39  	dataExecutionTest(b, tc)
    40  }
    41  
    42  // BenchmarkDataExecution_Baobab generates the data with Baobab network's database configurations.
    43  func BenchmarkDataExecution_Baobab(b *testing.B) {
    44  	tc := getExecutionTestDefaultTC()
    45  	tc.testName = "BenchmarkDataExecution_Baobab"
    46  	tc.originalDataDir = baobab500_orig
    47  
    48  	tc.dbc, tc.levelDBOption = genBaobabOptions()
    49  
    50  	dataExecutionTest(b, tc)
    51  }
    52  
    53  // BenchmarkDataExecution_CandidateLevelDB generates the data for main-net's
    54  // with candidate configurations, using LevelDB.
    55  func BenchmarkDataExecution_CandidateLevelDB(b *testing.B) {
    56  	tc := getExecutionTestDefaultTC()
    57  	tc.testName = "BenchmarkDataExecution_CandidateLevelDB"
    58  	tc.originalDataDir = candidate500LevelDB_orig
    59  
    60  	tc.dbc, tc.levelDBOption = genCandidateLevelDBOptions()
    61  
    62  	dataExecutionTest(b, tc)
    63  }
    64  
    65  // BenchmarkDataExecution_CandidateBadgerDB generates the data for main-net's
    66  // with candidate configurations, using BadgerDB.
    67  func BenchmarkDataExecution_CandidateBadgerDB(b *testing.B) {
    68  	tc := getExecutionTestDefaultTC()
    69  	tc.testName = "BenchmarkDataExecution_CandidateBadgerDB"
    70  	tc.originalDataDir = candidate500BadgerDB_orig
    71  
    72  	tc.dbc, tc.levelDBOption = genCandidateBadgerDBOptions()
    73  
    74  	dataExecutionTest(b, tc)
    75  }
    76  
    77  // BenchmarkDataExecution_Baobab_ControlGroup generates the data with Baobab network's database configurations.
    78  // To work as a control group, it only generates 10,000 accounts.
    79  func BenchmarkDataExecution_Baobab_ControlGroup(b *testing.B) {
    80  	tc := getExecutionTestDefaultTC()
    81  	tc.testName = "BenchmarkDataExecution_Baobab_ControlGroup"
    82  	tc.originalDataDir = baobab1_orig
    83  
    84  	tc.dbc, tc.levelDBOption = genBaobabOptions()
    85  
    86  	// ControlGroup specific setting
    87  	tc.numReceiversPerRun = 10000
    88  
    89  	dataExecutionTest(b, tc)
    90  }
    91  
    92  // Static variables, not to read same addresses and keys from files repeatedly.
    93  // If there are saved addresses and keys and the given numReceiversPerRun and testDataDir
    94  // match with saved ones, it will reuse saved addresses and keys.
    95  var savedAddresses []*common.Address = nil
    96  
    97  var (
    98  	savedKeys               []*ecdsa.PrivateKey = nil
    99  	savedNumReceiversPerRun int
   100  	savedTestDataDir        string
   101  )
   102  
   103  // dataExecutionTest is to check the performance of Klaytn with pre-generated data.
   104  // It generates warmUpTxs and executionTxs first, and then initialize blockchain and database to
   105  // remove any effects caused by generating transactions. And then it executes warmUpTxs and executionTxs.
   106  // To run the test, original data directory should be located at "$GOPATH/src/github.com/klaytn/"
   107  func dataExecutionTest(b *testing.B, tc *preGeneratedTC) {
   108  	testDataDir, profileFile, err := setUpTest(tc)
   109  	if err != nil {
   110  		b.Fatal(err)
   111  	}
   112  
   113  	///////////////////////////////////////////////////////////////////////////////////
   114  	///  Tx Generation Process. Generate warmUpTxs and executionTxs in this phase.  ///
   115  	///////////////////////////////////////////////////////////////////////////////////
   116  
   117  	// activeAddrs is used for both sender and receiver.
   118  	// len(activeAddrs) = tc.numReceiversPerRun
   119  	if savedAddresses == nil || savedKeys == nil || tc.numReceiversPerRun != savedNumReceiversPerRun || testDataDir != savedTestDataDir {
   120  		fmt.Println("Start reading addresses from files", "testDataDir", testDataDir, "numReceiversPerRun", tc.numReceiversPerRun)
   121  		savedAddresses, savedKeys, err = getAddrsAndKeysFromFile(tc.numReceiversPerRun, testDataDir, 0, tc.filePicker)
   122  		savedNumReceiversPerRun = tc.numReceiversPerRun
   123  		savedTestDataDir = testDataDir
   124  		if err != nil {
   125  			b.Fatal(err)
   126  		}
   127  		fmt.Println("End reading addresses from files")
   128  	} else {
   129  		fmt.Println("Reuse previously saved addresses and keys", "len(addrs)", len(savedAddresses), "len(keys)", len(savedKeys))
   130  	}
   131  
   132  	activeAddrs, activeKeys := savedAddresses, savedKeys
   133  
   134  	bcData, err := NewBCDataForPreGeneratedTest(testDataDir, tc)
   135  	if err != nil {
   136  		b.Fatal(err)
   137  	}
   138  
   139  	// Generate two different list of transactions, warmUpTxs and executionTxs
   140  	// warmUpTxs is to activate caching.
   141  	// executionTxs is to measure the performance of test after caching.
   142  	signer := types.MakeSigner(bcData.bc.Config(), bcData.bc.CurrentHeader().Number)
   143  	stateDB, err := bcData.bc.State()
   144  	if err != nil {
   145  		b.Fatal(err)
   146  	}
   147  
   148  	// len(warmUpTxs) = tc.numReceiversPerRun
   149  	warmUpTxs, nonceMap, err := makeTxsWithStateDB(false, stateDB, activeAddrs, activeKeys, activeAddrs, signer, tc.numReceiversPerRun, sequentialIndex)
   150  	if err != nil {
   151  		b.Fatal(err)
   152  	}
   153  
   154  	// len(executionTxs) = tc.numTxsPerGen
   155  	executionTxs, _, err := makeTxsWithNonceMap(false, nonceMap, activeAddrs, activeKeys, activeAddrs, signer, tc.numTxsPerGen, randomIndex)
   156  	if err != nil {
   157  		b.Fatal(err)
   158  	}
   159  
   160  	fmt.Println("len(warmUpTxs)", len(warmUpTxs), "len(executionTxs)", len(executionTxs))
   161  
   162  	bcData.bc.Stop()
   163  	bcData.db.Close()
   164  
   165  	/////////////////////////////////////////////////////////////////////////////////////////////
   166  	///  Tx Execution Process. Shutdown and initialize DB to execute txs in fresh condition.  ///
   167  	/////////////////////////////////////////////////////////////////////////////////////////////
   168  	fmt.Println("Re-setting up test data dir to make database to initial condition.")
   169  	testDataDir, err = setupTestDir(tc.originalDataDir, tc.isGenerateTest)
   170  	if err != nil {
   171  		b.Fatalf("err: %v, dir: %v", err, testDataDir)
   172  	}
   173  
   174  	bcData, err = NewBCDataForPreGeneratedTest(testDataDir, tc)
   175  	if err != nil {
   176  		b.Fatal(err)
   177  	}
   178  	defer bcData.db.Close()
   179  	defer bcData.bc.Stop()
   180  
   181  	signer = types.MakeSigner(bcData.bc.Config(), bcData.bc.CurrentHeader().Number)
   182  	stateDB, err = bcData.bc.State()
   183  	if err != nil {
   184  		b.Fatal(err)
   185  	}
   186  
   187  	fmt.Println("Call AsMessageWithAccountKeyPicker for warmUpTxs")
   188  	for _, tx := range warmUpTxs {
   189  		if _, err = tx.AsMessageWithAccountKeyPicker(signer, stateDB, bcData.bc.CurrentBlock().NumberU64()); err != nil {
   190  			b.Fatal(err)
   191  		}
   192  	}
   193  
   194  	fmt.Println("Call AsMessageWithAccountKeyPicker for executionTxs")
   195  	for _, tx := range executionTxs {
   196  		if _, err = tx.AsMessageWithAccountKeyPicker(signer, stateDB, bcData.bc.CurrentBlock().NumberU64()); err != nil {
   197  			b.Fatal(err)
   198  		}
   199  	}
   200  
   201  	txPool := makeTxPool(bcData, txPoolSize)
   202  
   203  	// Run warmUpTxs.
   204  	fmt.Println("Start warming-up phase")
   205  	if err := executeTxs(bcData, txPool, warmUpTxs); err != nil {
   206  		b.Fatal(err)
   207  	}
   208  	fmt.Println("End warming-up phase. Start execution phase.")
   209  	// Run executionTxs and measure the execution time and profiling.
   210  	// Start timer and profiler from here.
   211  	b.ResetTimer()
   212  	b.StartTimer()
   213  	defer b.StopTimer()
   214  	pprof.StartCPUProfile(profileFile)
   215  	defer pprof.StopCPUProfile()
   216  
   217  	if err := executeTxs(bcData, txPool, executionTxs); err != nil {
   218  		b.Fatal(err)
   219  	}
   220  }
   221  
   222  // executeTxs repeats pushing and executing certain number of transactions until
   223  // there is no transaction left.
   224  func executeTxs(bcData *BCData, txPool *blockchain.TxPool, txs types.Transactions) error {
   225  	for i := 0; i < len(txs); i += txPoolSize {
   226  		end := i + txPoolSize
   227  		if end > len(txs) {
   228  			end = len(txs)
   229  		}
   230  		txPool.AddRemotes(txs[i:end])
   231  		for {
   232  			if err := bcData.GenABlockWithTxPoolWithoutAccountMap(txPool); err != nil {
   233  				if err == errEmptyPending {
   234  					break
   235  				}
   236  				return err
   237  			}
   238  		}
   239  	}
   240  	return nil
   241  }
   242  
   243  // getExecutionTestDefaultTC returns default TC of data execution tests.
   244  func getExecutionTestDefaultTC() *preGeneratedTC {
   245  	numActiveAccounts := 100 * 10000
   246  	numExecPhaseTxs := txPoolSize * 10
   247  
   248  	return &preGeneratedTC{
   249  		isGenerateTest:     false,
   250  		numReceiversPerRun: numActiveAccounts, // number of accounts used for warming-up phase, which means "active accounts"
   251  		numTxsPerGen:       numExecPhaseTxs,   // number of transactions executed during execution phase
   252  		numTotalSenders:    10000,
   253  		filePicker:         sequentialIndex,
   254  		addrPicker:         sequentialIndex,
   255  		cacheConfig:        defaultCacheConfig(),
   256  	}
   257  }