github.com/koko1123/flow-go-1@v0.29.6/engine/execution/computation/manager_benchmark_test.go (about)

     1  package computation
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ipfs/go-datastore"
    11  	dssync "github.com/ipfs/go-datastore/sync"
    12  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    13  	"github.com/onflow/cadence/runtime"
    14  	"github.com/rs/zerolog"
    15  	"github.com/stretchr/testify/mock"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/koko1123/flow-go-1/engine/execution/computation/committer"
    19  	"github.com/koko1123/flow-go-1/engine/execution/computation/computer"
    20  	"github.com/koko1123/flow-go-1/engine/execution/state/delta"
    21  	"github.com/koko1123/flow-go-1/engine/execution/testutil"
    22  	"github.com/koko1123/flow-go-1/fvm"
    23  	"github.com/koko1123/flow-go-1/fvm/derived"
    24  	reusableRuntime "github.com/koko1123/flow-go-1/fvm/runtime"
    25  	"github.com/koko1123/flow-go-1/fvm/state"
    26  	"github.com/koko1123/flow-go-1/model/flow"
    27  	"github.com/koko1123/flow-go-1/module/executiondatasync/execution_data"
    28  	exedataprovider "github.com/koko1123/flow-go-1/module/executiondatasync/provider"
    29  	mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock"
    30  	"github.com/koko1123/flow-go-1/module/mempool/entity"
    31  	"github.com/koko1123/flow-go-1/module/metrics"
    32  	module "github.com/koko1123/flow-go-1/module/mock"
    33  	requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest"
    34  	"github.com/koko1123/flow-go-1/module/trace"
    35  	"github.com/koko1123/flow-go-1/utils/unittest"
    36  )
    37  
    38  type testAccount struct {
    39  	address    flow.Address
    40  	privateKey flow.AccountPrivateKey
    41  }
    42  
    43  type testAccounts struct {
    44  	accounts []testAccount
    45  	seq      uint64
    46  }
    47  
    48  func createAccounts(b *testing.B, vm fvm.VM, ledger state.View, num int) *testAccounts {
    49  	privateKeys, err := testutil.GenerateAccountPrivateKeys(num)
    50  	require.NoError(b, err)
    51  
    52  	addresses, err := testutil.CreateAccounts(
    53  		vm,
    54  		ledger,
    55  		derived.NewEmptyDerivedBlockData(),
    56  		privateKeys,
    57  		chain)
    58  	require.NoError(b, err)
    59  
    60  	accs := &testAccounts{
    61  		accounts: make([]testAccount, num),
    62  	}
    63  	for i := 0; i < num; i++ {
    64  		accs.accounts[i] = testAccount{
    65  			address:    addresses[i],
    66  			privateKey: privateKeys[i],
    67  		}
    68  	}
    69  	return accs
    70  }
    71  
    72  func mustFundAccounts(
    73  	b *testing.B,
    74  	vm fvm.VM,
    75  	ledger state.View,
    76  	execCtx fvm.Context,
    77  	accs *testAccounts,
    78  ) {
    79  	derivedBlockData := derived.NewEmptyDerivedBlockData()
    80  	execCtx = fvm.NewContextFromParent(
    81  		execCtx,
    82  		fvm.WithDerivedBlockData(derivedBlockData))
    83  
    84  	var err error
    85  	for _, acc := range accs.accounts {
    86  		transferTx := testutil.CreateTokenTransferTransaction(chain, 1_000_000, acc.address, chain.ServiceAddress())
    87  		err = testutil.SignTransactionAsServiceAccount(transferTx, accs.seq, chain)
    88  		require.NoError(b, err)
    89  		accs.seq++
    90  
    91  		tx := fvm.Transaction(
    92  			transferTx,
    93  			derivedBlockData.NextTxIndexForTestingOnly())
    94  		err = vm.Run(execCtx, tx, ledger)
    95  		require.NoError(b, err)
    96  		require.NoError(b, tx.Err)
    97  	}
    98  }
    99  
   100  func BenchmarkComputeBlock(b *testing.B) {
   101  	b.StopTimer()
   102  
   103  	tracer, err := trace.NewTracer(zerolog.Nop(), "", "", 4)
   104  	require.NoError(b, err)
   105  
   106  	vm := fvm.NewVirtualMachine()
   107  
   108  	chain := flow.Emulator.Chain()
   109  	execCtx := fvm.NewContext(
   110  		fvm.WithChain(chain),
   111  		fvm.WithAccountStorageLimit(true),
   112  		fvm.WithTransactionFeesEnabled(true),
   113  		fvm.WithTracer(tracer),
   114  		fvm.WithReusableCadenceRuntimePool(
   115  			reusableRuntime.NewReusableCadenceRuntimePool(
   116  				ReusableCadenceRuntimePoolSize,
   117  				runtime.Config{})),
   118  	)
   119  	ledger := testutil.RootBootstrappedLedger(
   120  		vm,
   121  		execCtx,
   122  		fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee),
   123  		fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation),
   124  		fvm.WithTransactionFee(fvm.DefaultTransactionFees),
   125  		fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW),
   126  	)
   127  	accs := createAccounts(b, vm, ledger, 1000)
   128  	mustFundAccounts(b, vm, ledger, execCtx, accs)
   129  
   130  	me := new(module.Local)
   131  	me.On("NodeID").Return(flow.ZeroID)
   132  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   133  		Return(nil, nil)
   134  
   135  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   136  	trackerStorage := mocktracker.NewMockStorage()
   137  
   138  	prov := exedataprovider.NewProvider(
   139  		zerolog.Nop(),
   140  		metrics.NewNoopCollector(),
   141  		execution_data.DefaultSerializer,
   142  		bservice,
   143  		trackerStorage,
   144  	)
   145  
   146  	// TODO(rbtz): add real ledger
   147  	blockComputer, err := computer.NewBlockComputer(
   148  		vm,
   149  		execCtx,
   150  		metrics.NewNoopCollector(),
   151  		tracer,
   152  		zerolog.Nop(),
   153  		committer.NewNoopViewCommitter(),
   154  		me,
   155  		prov)
   156  	require.NoError(b, err)
   157  
   158  	derivedChainData, err := derived.NewDerivedChainData(
   159  		derived.DefaultDerivedDataCacheSize)
   160  	require.NoError(b, err)
   161  
   162  	engine := &Manager{
   163  		blockComputer:    blockComputer,
   164  		tracer:           tracer,
   165  		me:               me,
   166  		derivedChainData: derivedChainData,
   167  	}
   168  
   169  	view := delta.NewView(ledger.Get)
   170  	blockView := view.NewChild()
   171  
   172  	b.SetParallelism(1)
   173  
   174  	parentBlock := &flow.Block{
   175  		Header:  &flow.Header{},
   176  		Payload: &flow.Payload{},
   177  	}
   178  
   179  	const (
   180  		cols = 16
   181  		txes = 128
   182  	)
   183  
   184  	b.Run(fmt.Sprintf("%d/cols/%d/txes", cols, txes), func(b *testing.B) {
   185  		b.StopTimer()
   186  		b.ResetTimer()
   187  
   188  		var elapsed time.Duration
   189  		for i := 0; i < b.N; i++ {
   190  			executableBlock := createBlock(b, parentBlock, accs, cols, txes)
   191  			parentBlock = executableBlock.Block
   192  
   193  			b.StartTimer()
   194  			start := time.Now()
   195  			res, err := engine.ComputeBlock(context.Background(), executableBlock, blockView)
   196  			elapsed += time.Since(start)
   197  			b.StopTimer()
   198  
   199  			require.NoError(b, err)
   200  			for j, r := range res.TransactionResults {
   201  				// skip system transactions
   202  				if j >= cols*txes {
   203  					break
   204  				}
   205  				require.Emptyf(b, r.ErrorMessage, "Transaction %d failed", j)
   206  			}
   207  		}
   208  		totalTxes := int64(cols) * int64(txes) * int64(b.N)
   209  		b.ReportMetric(float64(elapsed.Nanoseconds()/totalTxes/int64(time.Microsecond)), "us/tx")
   210  	})
   211  }
   212  
   213  func createBlock(b *testing.B, parentBlock *flow.Block, accs *testAccounts, colNum int, txNum int) *entity.ExecutableBlock {
   214  	completeCollections := make(map[flow.Identifier]*entity.CompleteCollection, colNum)
   215  	collections := make([]*flow.Collection, colNum)
   216  	guarantees := make([]*flow.CollectionGuarantee, colNum)
   217  
   218  	for c := 0; c < colNum; c++ {
   219  		transactions := make([]*flow.TransactionBody, txNum)
   220  		for t := 0; t < txNum; t++ {
   221  			transactions[t] = createTokenTransferTransaction(b, accs)
   222  		}
   223  
   224  		collection := &flow.Collection{Transactions: transactions}
   225  		guarantee := &flow.CollectionGuarantee{CollectionID: collection.ID()}
   226  
   227  		collections[c] = collection
   228  		guarantees[c] = guarantee
   229  		completeCollections[guarantee.ID()] = &entity.CompleteCollection{
   230  			Guarantee:    guarantee,
   231  			Transactions: transactions,
   232  		}
   233  	}
   234  
   235  	block := flow.Block{
   236  		Header: &flow.Header{
   237  			ParentID: parentBlock.ID(),
   238  			View:     parentBlock.Header.Height + 1,
   239  		},
   240  		Payload: &flow.Payload{
   241  			Guarantees: guarantees,
   242  		},
   243  	}
   244  
   245  	return &entity.ExecutableBlock{
   246  		Block:               &block,
   247  		CompleteCollections: completeCollections,
   248  		StartState:          unittest.StateCommitmentPointerFixture(),
   249  	}
   250  }
   251  
   252  func createTokenTransferTransaction(b *testing.B, accs *testAccounts) *flow.TransactionBody {
   253  	var err error
   254  
   255  	rnd := rand.Intn(len(accs.accounts))
   256  	src := accs.accounts[rnd]
   257  	dst := accs.accounts[(rnd+1)%len(accs.accounts)]
   258  
   259  	tx := testutil.CreateTokenTransferTransaction(chain, 1, dst.address, src.address)
   260  	tx.SetProposalKey(chain.ServiceAddress(), 0, accs.seq).
   261  		SetGasLimit(1000).
   262  		SetPayer(chain.ServiceAddress())
   263  	accs.seq++
   264  
   265  	err = testutil.SignPayload(tx, src.address, src.privateKey)
   266  	require.NoError(b, err)
   267  
   268  	err = testutil.SignEnvelope(tx, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
   269  	require.NoError(b, err)
   270  
   271  	return tx
   272  }