Skip to content
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

EVM benchmark tests #142

Merged
merged 13 commits into from
Mar 5, 2024
32 changes: 32 additions & 0 deletions .github/workflows/benchmark-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
name: Benchmark Tests
on: # yamllint disable-line rule:truthy
workflow_call:
outputs:
workflow_output:
description: "Benchmark Tests output"
value: ${{ jobs.benchmark_test.outputs.test_output_failure }}

jobs:
benchmark_test:
name: Run Benchmark Tests
runs-on: ubuntu-latest
outputs:
test_output_failure: ${{ steps.run_tests_failure.outputs.test_output }}
steps:
- name: Checkout Code
uses: actions/[email protected]
with:
submodules: recursive
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Setup Go
uses: actions/[email protected]
with:
go-version: 1.20.x
- name: Run Go Test
run: make benchmark-test
- name: Run Go Test Failed
if: failure()
id: run_tests_failure
run: echo "test_output=false" >> $GITHUB_OUTPUT

19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ on: # yamllint disable-line rule:truthy
description: Fuzz Tests
type: boolean
default: true
benchmark-test:
description: Benchmark Tests
type: boolean
default: true
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved
workflow_call:
inputs:
build-blade:
Expand Down Expand Up @@ -65,6 +69,10 @@ on: # yamllint disable-line rule:truthy
description: Fuzz Tests
type: boolean
required: true
benchmark-test:
description: Benchmark Tests
type: boolean
default: true
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved
outputs:
build-blade:
description: Build Blade output
Expand All @@ -87,6 +95,9 @@ on: # yamllint disable-line rule:truthy
fuzz-test:
description: Fuzz Tests output
value: ${{ jobs.fuzz-test.outputs.workflow_output }}
benchmark-test:
description: Benchmark Tests output
value: ${{ jobs.benchmark-test.outputs.workflow_output }}

jobs:
build-blade:
Expand Down Expand Up @@ -144,3 +155,11 @@ jobs:
inputs.fuzz-test ||
github.event_name == 'pull_request' ||
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'))
benchmark-test:
name: Benchmark Tests
uses: ./.github/workflows/benchmark-test.yml
needs: build-blade
if: |
inputs.benchmark-test ||
github.event_name == 'pull_request' ||
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'))
3 changes: 3 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: Nightly Build
on: # yamllint disable-line rule:truthy
workflow_dispatch:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: '0 0 * * *'
Expand Down Expand Up @@ -30,6 +31,7 @@ jobs:
e2e-legacy-test: true
property-polybft-test: true
fuzz-test: true
benchmark-test: true
deploy_network:
name: Deploy Network
uses: ./.github/workflows/deploy-network.yml
Expand Down Expand Up @@ -120,6 +122,7 @@ jobs:
e2e_legacy_test_output: ${{ needs.ci.outputs.e2e-legacy-test }}
property_polybft_test_output: ${{ needs.ci.outputs.property-polybft-test }}
fuzz_test_output: ${{ needs.ci.outputs.fuzz-test }}
benchmark_test_output: ${{ needs.ci.outputs.benchmark-test }}
deploy_network_terraform_output: ${{ needs.deploy_network.outputs.terraform_output }}
deploy_network_ansible_output: ${{ needs.deploy_network.outputs.ansible_output }}
load_test_multiple_eoa_output: ${{ needs.load_test_multiple_eoa.outputs.load_test_output }}
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/notification-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ on: # yamllint disable-line rule:truthy
description: Fuzz Tests output
type: string
required: true
benchmark_test_output:
description: Benchmark Tests output
type: string
required: true
deploy_network_terraform_output:
description: Deploy Network - Terraform output
type: string
Expand Down Expand Up @@ -91,7 +95,7 @@ jobs:
{
"attachments": [
{
"color": "${{ inputs.build_blade_output == '' && inputs.lint_output == '' && inputs.unit_test_output == '' && inputs.e2e_polybft_test_output == '' && inputs.e2e_legacy_test_output == '' && inputs.property_polybft_test_output == '' && inputs.fuzz_test_output == '' && inputs.deploy_network_terraform_output == '' && inputs.deploy_network_ansible_output == '' && inputs.load_test_multiple_eoa_output == 'true' && inputs.load_test_multiple_erc20_output == 'true' && inputs.destroy_network_logs_output == '' && inputs.destroy_network_terraform_output == '' && env.green_color || env.red_color }}",
"color": "${{ inputs.build_blade_output == '' && inputs.lint_output == '' && inputs.unit_test_output == '' && inputs.e2e_polybft_test_output == '' && inputs.e2e_legacy_test_output == '' && inputs.property_polybft_test_output == '' && inputs.fuzz_test_output == '' && inputs.benchmark_test_output == '' && inputs.deploy_network_terraform_output == '' && inputs.deploy_network_ansible_output == '' && inputs.load_test_multiple_eoa_output == 'true' && inputs.load_test_multiple_erc20_output == 'true' && inputs.destroy_network_logs_output == '' && inputs.destroy_network_terraform_output == '' && env.green_color || env.red_color }}",
"blocks": [
{
"type": "header",
Expand Down Expand Up @@ -152,13 +156,13 @@ jobs:
]
},
{
"color": "${{ inputs.build_blade_output == '' && inputs.lint_output == '' && inputs.unit_test_output == '' && inputs.fuzz_test_output == '' && inputs.e2e_legacy_test_output == '' && inputs.e2e_polybft_test_output == '' && inputs.property_polybft_test_output == '' && env.green_color || env.red_color }}",
"color": "${{ inputs.build_blade_output == '' && inputs.lint_output == '' && inputs.unit_test_output == '' && inputs.fuzz_test_output == '' && inputs.benchmark_test_output == '' && inputs.e2e_legacy_test_output == '' && inputs.e2e_polybft_test_output == '' && inputs.property_polybft_test_output == '' && env.green_color || env.red_color }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*CI*\n${{ inputs.build_blade_output == '' && 'Build Blade' || '~Build Blade~' }}, ${{ inputs.lint_output == '' && 'Lint' || '~Lint~' }}, ${{ inputs.unit_test_output == '' && 'Unit Tests' || '~Unit Tests~' }},\n${{ inputs.fuzz_test_output == '' && 'Fuzz Tests' || '~Fuzz Tests~' }}, ${{ inputs.e2e_legacy_test_output == '' && 'E2E Legacy Tests' || '~E2E Legacy Tests~' }},\n${{ inputs.e2e_polybft_test_output == '' && 'E2E PolyBFT Tests' || '~E2E PolyBFT Tests~' }}, ${{ inputs.property_polybft_test_output == '' && 'Property PolyBFT Tests' || '~Property PolyBFT Tests~' }}"
"text": "*CI*\n${{ inputs.build_blade_output == '' && 'Build' || '~Build~' }}, ${{ inputs.lint_output == '' && 'Lint' || '~Lint~' }}, ${{ inputs.unit_test_output == '' && 'Unit Tests' || '~Unit Tests~' }},\n${{ inputs.fuzz_test_output == '' && 'Fuzz Tests' || '~Fuzz Tests~' }}, ${{ inputs.e2e_legacy_test_output == '' && 'E2E Legacy Tests' || '~E2E Legacy Tests~' }},\n${{ inputs.e2e_polybft_test_output == '' && 'E2E PolyBFT Tests' || '~E2E PolyBFT Tests~' }}, ${{ inputs.property_polybft_test_output == '' && 'Property PolyBFT Tests' || '~Property PolyBFT Tests~' }},\n${{ inputs.benchmark_test_output == '' && 'Benchmark Tests' || '~Benchmark Tests~' }}"
}
}
]
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
[submodule "blade-contracts"]
path = blade-contracts
url = https://github.com/Ethernal-Tech/blade-contracts
[submodule "tests/evm-benchmarks"]
path = tests/evm-benchmarks
url = https://github.com/ipsilon/evm-benchmarks
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ generate-bsd-licenses: check-git

.PHONY: unit-test
unit-test: check-go
go test -race -shuffle=on -coverprofile coverage.out -timeout 20m `go list ./... | grep -v e2e`
go test -race -shuffle=on -coverprofile coverage.out -v -timeout 20m `go list ./... | grep -v e2e`
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved

.PHONY: benchmark-test
benchmark-test: check-go
go test -bench=. -run=^$ `go list ./... | grep -v /e2e`
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved

.PHONY: fuzz-test
fuzz-test: check-go
Expand Down
19 changes: 18 additions & 1 deletion state/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,8 @@ func (t *Transition) apply(msg *types.Transaction) (*runtime.ExecutionResult, er

// set up initial access list
initialAccessList := runtime.NewAccessList()
if t.config.Berlin { // check if berlin fork is activated or not
if t.config.Berlin {
// populate access list in case Berlin fork is active
initialAccessList.PrepareAccessList(msg.From(), msg.To(), t.precompiles.Addrs, msg.AccessList())
}

Expand Down Expand Up @@ -1183,6 +1184,11 @@ func (t *Transition) GetRefund() uint64 {
return t.state.GetRefund()
}

// ActivePrecompiles returns addresses of precompile contracts
func (t *Transition) ActivePrecompiles() []types.Address {
return t.precompiles.Addrs
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved
}

func TransactionGasCost(msg *types.Transaction, isHomestead, isIstanbul bool) (uint64, error) {
cost := uint64(0)

Expand Down Expand Up @@ -1354,6 +1360,17 @@ func (t *Transition) RevertToSnapshot(snapshot int) error {
return nil
}

// PopulateAccessList populates access list based on the provided access list
func (t *Transition) PopulateAccessList(acl types.TxAccessList) {
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved
for _, accessInfo := range acl {
t.AddAddressToAccessList(accessInfo.Address)

for _, storageKey := range accessInfo.StorageKeys {
t.AddSlotToAccessList(accessInfo.Address, storageKey)
}
}
}

func (t *Transition) AddSlotToAccessList(addr types.Address, slot types.Hash) {
t.journal.Append(&runtime.AccessListAddSlotChange{Address: addr, Slot: slot})
t.accessList.AddSlot(addr, slot)
Expand Down
1 change: 1 addition & 0 deletions tests/evm-benchmarks
Submodule evm-benchmarks added at d8b88f
173 changes: 173 additions & 0 deletions tests/evm_benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package tests

import (
"encoding/json"
"fmt"
"math/big"
"os"
"testing"
"time"

"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"

"github.com/0xPolygon/polygon-edge/chain"
"github.com/0xPolygon/polygon-edge/crypto"
"github.com/0xPolygon/polygon-edge/state"
"github.com/0xPolygon/polygon-edge/types"
)

const (
benchmarksDir = "evm-benchmarks/benchmarks"
chainID = 10
)

func BenchmarkEVM(b *testing.B) {
folders, err := listFolders([]string{benchmarksDir})
require.NoError(b, err)

for _, folder := range folders {
files, err := listFiles(folder, ".json")
require.NoError(b, err)

for _, file := range files {
name := getTestName(file)

b.Run(name, func(b *testing.B) {
data, err := os.ReadFile(file)
require.NoError(b, err)

var testCases map[string]testCase
if err = json.Unmarshal(data, &testCases); err != nil {
b.Fatalf("failed to unmarshal %s: %v", file, err)
}

for _, tc := range testCases {
for fork, postState := range tc.Post {
forks, exists := Forks[fork]
if !exists {
b.Logf("%s fork is not supported, skipping test case.", fork)
continue
}

fc := &forkConfig{name: fork, forks: forks}

for idx, postStateEntry := range postState {
err := runBenchmarkTest(b, tc, fc, postStateEntry)
require.NoError(b, err, fmt.Sprintf("test %s (case#%d) execution failed", getTestName(file), idx))
Stefan-Ethernal marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
})
}
}
}

func runBenchmarkTest(b *testing.B, c testCase, fc *forkConfig, p postEntry) error {
env := c.Env.ToEnv(b)
forks := fc.forks

var baseFee *big.Int

if forks.IsActive(chain.London, 0) {
if c.Env.BaseFee != "" {
baseFee = stringToBigIntT(b, c.Env.BaseFee)
} else {
// Retesteth uses `10` for genesis baseFee. Therefore, it defaults to
// parent - 2 : 0xa as the basefee for 'this' context.
baseFee = big.NewInt(testGenesisBaseFee)
}
}

msg, err := c.Transaction.At(p.Indexes, baseFee)
if err != nil {
return err
}

s, _, parentRoot, err := buildState(c.Pre)
if err != nil {
return err
}

currentForks := forks.At(uint64(env.Number))

// try to recover tx with current signer
if len(p.TxBytes) != 0 {
tx := &types.Transaction{}
err := tx.UnmarshalRLP(p.TxBytes)
if err != nil {
return err
}

signer := crypto.NewSigner(currentForks, chainID)

_, err = signer.Sender(tx)
if err != nil {
return err
}
}

executor := state.NewExecutor(&chain.Params{
Forks: forks,
ChainID: chainID,
BurnContract: map[uint64]types.Address{
0: types.ZeroAddress,
},
}, s, hclog.NewNullLogger())

executor.GetHash = func(*types.Header) func(i uint64) types.Hash {
return vmTestBlockHash
}

transition, err := executor.BeginTxn(parentRoot, c.Env.ToHeader(b), env.Coinbase)
if err != nil {
return err
}

var (
gasUsed uint64
elapsed uint64
refund uint64
)

b.ResetTimer()
for n := 0; n < b.N; n++ {
snapshotID := transition.Snapshot()
if currentForks.Berlin {
transition.PopulateAccessList(msg.AccessList())
transition.AddAddressToAccessList(msg.From())
if msg.To() != nil {
transition.AddAddressToAccessList(*msg.To())
}
}

b.StartTimer()
start := time.Now()

// execute the message
result := transition.Call2(msg.From(), *msg.To(), msg.Input(), msg.Value(), msg.Gas())
if result.Err != nil {
return result.Err
}

b.StopTimer()
elapsed += uint64(time.Since(start))
refund += transition.GetRefund()
gasUsed += msg.Gas() - result.GasLeft

err = transition.RevertToSnapshot(snapshotID)
if err != nil {
return err
}
}

if elapsed < 1 {
elapsed = 1
}

// Keep it as uint64, multiply 100 to get two digit float later
mgasps := (100 * 1000 * (gasUsed - refund)) / elapsed
b.ReportMetric(float64(mgasps)/100, "mgas/s")

return nil
}
Loading
Loading