-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Block Benchmark Suite #1955
base: main
Are you sure you want to change the base?
Conversation
chain/chaintest/block.go
Outdated
switch numOfTxsPerBlock { | ||
case 0: | ||
timestampOffset = rules.GetMinEmptyBlockGap() | ||
default: | ||
timestampOffset = rules.GetMinBlockGap() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we handle this with a single if block instead of a switch?
Not identical, but this is a case of "unnecessary else" - https://github.com/uber-go/guide/blob/master/style.md#unnecessary-else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
chain/chaintest/block.go
Outdated
factories := []chain.AuthFactory{} | ||
customAllocs := []*genesis.CustomAllocation{} | ||
for range test.NumOfTxsPerBlock { | ||
pk, err := ed25519.GeneratePrivateKey() | ||
r.NoError(err) | ||
|
||
factory := auth.NewED25519Factory(pk) | ||
factories = append(factories, factory) | ||
|
||
customAllocs = append(customAllocs, &genesis.CustomAllocation{ | ||
Address: factory.Address(), | ||
Balance: uint64(allocBalance), | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we leave the auth factories behind the generator interface? This is a VM-level detail that will have a non-trivial performance impact.
tsv := ts.NewView(state.CompletePermissions, parentView, 0) | ||
if err := tsv.Insert(ctx, chain.HeightKey(metadataManager.HeightPrefix()), binary.BigEndian.AppendUint64(nil, height)); err != nil { | ||
return nil, err | ||
} | ||
if err := tsv.Insert(ctx, chain.TimestampKey(metadataManager.TimestampPrefix()), binary.BigEndian.AppendUint64(nil, uint64(timestamp))); err != nil { | ||
return nil, err | ||
} | ||
if err := tsv.Insert(ctx, chain.FeeKey(metadataManager.FeePrefix()), feeManager.Bytes()); err != nil { | ||
return nil, err | ||
} | ||
tsv.Commit() | ||
|
||
root, err := parentView.GetMerkleRoot(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
parentView, err = parentView.NewView(ctx, merkledb.ViewChanges{ | ||
MapOps: ts.ChangedKeys(), | ||
ConsumeBytes: true, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
statelessBlock, err := chain.NewStatelessBlock( | ||
parentID, | ||
timestamp, | ||
height, | ||
txs, | ||
root, | ||
nil, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
parentID = statelessBlock.GetID() | ||
|
||
blk := chain.NewExecutionBlock(statelessBlock) | ||
executionBlocks[i] = blk | ||
} | ||
return executionBlocks, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is getting duplicated across a growing number of places - can we make this more re-usable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For DSMR - we need the ability to assemble a block given a parent state / block, metadata for the next block (assigned from DSMR wrapper block), and a set of transactions that should be included. Maybe we can implement an Assembler
and use that functionality here
chain/chaintest/block.go
Outdated
var view merkledb.View | ||
view = db | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
for j := 0; j < int(test.NumOfBlocks); j++ { | ||
outputBlock, err := processor.Execute( | ||
ctx, | ||
view, | ||
blocks[j], | ||
false, | ||
) | ||
r.NoError(err) | ||
view = outputBlock.View | ||
} | ||
view = db | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a comment explaining the handling of views here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
chain/chaintest/block.go
Outdated
ctx, | ||
view, | ||
blocks[j], | ||
false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use false
here? In normal operation, this will be set to true and we will need to check the expiry window
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to true
.
chain/chaintest/block.go
Outdated
switch test.AuthVerificationCores { | ||
case 0, 1: | ||
processorWorkers = workers.NewSerial() | ||
default: | ||
processorWorkers = workers.NewParallel( | ||
test.AuthVerificationCores, | ||
numOfAuthVerificationJobs, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a simple if condition instead of a switch since there are only two options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
chain/chaintest/block.go
Outdated
customAllocs = append(customAllocs, &genesis.CustomAllocation{ | ||
Address: factory.Address(), | ||
Balance: uint64(allocBalance), | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm - this limits the usability of this package to VMs that use the default genesis / CustomAllocation
types.
It seems like the caller should define their application logic including the genesis, transactions (auth/actions), and pass in the components they would have control over as opposed to making this a responsibility of the benchmark tool
Regarding #1955 (comment) and #1955 (comment), I've decided to switch from users providing a tx-based helper (
|
chain/processor_test.go
Outdated
MetadataManager: metadata.NewDefaultManager(), | ||
BalanceHandler: &mockBalanceHandler{}, | ||
AuthVM: &chaintest.TestAuthVM{ | ||
GetAuthBatchVerifierF: func(authTypeID uint8, cores int, count int) (chain.AuthBatchVerifier, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense to have a single copy of the GetAuthBatchVerifierF
function and use it across these three locations ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to wait until #1960 is merged to address this.
chain/processor_test.go
Outdated
test.Run(context.Background(), b) | ||
} | ||
|
||
func BenchmarkExecuteBlocksZipfTxs(b *testing.B) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given that all these benchmarks are almost identical, I'd opt into having a single benchmark containing multiple sub-benchmarks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, switched to using table benchmarks (looks much better as well!)
This PR adds a benchmarking test suite for benchmarking the processor execution time of a list of blocks. This test suite can be used for benchmarking both the HyperSDK itself and any custom VM which wants to benchmark its own block execution time.
As part of the test suite, users are required to pass in a
TxListGenerator
which when called, returns a list of transactions that will be set to the transaction list of a block. An implementation ofTxListGenerator
can be used to benchmark the HyperSDK's pessimistic concurrency model against various state access distributions (i.e. TXs with no conflicting state accesses, TXs with all conflicting state accesses, TXs with state accesses under a Zipf distribution).An example of how benchmarking would look like for the HyperSDK (with empty blocks) is as follows: