Skip to content

Commit

Permalink
feat(benchmarks): runtime utility function + timestamp set benchmarks (
Browse files Browse the repository at this point in the history
…#330)

* feat(benchmarks): export Benchmark_run utility function
  • Loading branch information
radkomih authored Jan 16, 2024
1 parent d13953a commit 768722f
Show file tree
Hide file tree
Showing 14 changed files with 629 additions and 21 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ build-dev: build-tinygo
@echo "Building \"runtime.wasm\""; \
WASMOPT="$(CURRENT_DIR)/$(WASMOPT_PATH)" $(TINYGO_BUILD_COMMAND) -o=$(BUILD_PATH) runtime/runtime.go

build-benchmarks: build-tinygo
@echo "Building \"runtime.wasm\" (no-debug)"; \
WASMOPT="$(CURRENT_DIR)/$(WASMOPT_PATH)" $(TINYGO_BUILD_COMMAND_NODEBUG) -tags benchmarks -o=$(BUILD_PATH) runtime/runtime.go

start-network:
cp build/runtime.wasm polkadot-sdk/substrate/bin/node-template/runtime.wasm; \
cd polkadot-sdk/substrate/bin/node-template/node; \
Expand All @@ -94,4 +98,7 @@ test-integration:

test-coverage:
@set -e; \
./scripts/coverage.sh
./scripts/coverage.sh

benchmark:
@GOMAXPROCS=1 go test --tags=nonwasmenv -run=XXX -bench=. -benchtime=20x ./runtime/...
187 changes: 187 additions & 0 deletions api/benchmarking/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package benchmarking

import (
"bytes"

sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/execution/types"
"github.com/LimeChain/gosemble/frame/support"
"github.com/LimeChain/gosemble/frame/system"
"github.com/LimeChain/gosemble/primitives/benchmarking"
"github.com/LimeChain/gosemble/primitives/io"
"github.com/LimeChain/gosemble/primitives/log"
primitives "github.com/LimeChain/gosemble/primitives/types"
"github.com/LimeChain/gosemble/utils"
)

type Module struct {
systemModule system.Module
transactional support.Transactional[primitives.PostDispatchInfo]
decoder types.RuntimeDecoder
memUtils utils.WasmMemoryTranslator
hashing io.Hashing
logger log.Logger
}

func New(systemModule system.Module, decoder types.RuntimeDecoder, logger log.Logger) Module {
return Module{
systemModule: systemModule,
decoder: decoder,
transactional: support.NewTransactional[primitives.PostDispatchInfo](logger),
memUtils: utils.NewMemoryTranslator(),
hashing: io.NewHashing(),
logger: logger,
}
}

// TODO:
// Implement DbCommit, DbWipe once the state implementation
// in Gossamer supports caching and nested transactions.
// https://github.com/ChainSafe/gossamer/discussions/3646

func (m Module) Run(dataPtr int32, dataLen int32) int64 {
data := m.memUtils.GetWasmMemorySlice(dataPtr, dataLen)
buffer := bytes.NewBuffer(data)

benchmarkConfig, err := benchmarking.DecodeBenchmarkConfig(buffer)
if err != nil {
m.logger.Critical(err.Error())
}

opaqueExtrinsic := sc.SequenceU8ToBytes(benchmarkConfig.Extrinsic)
extrinsic, err := m.decoder.DecodeUncheckedExtrinsic(bytes.NewBuffer(opaqueExtrinsic))
if err != nil {
m.logger.Critical(err.Error())
}

function := extrinsic.Function()
args := function.Args()
accountId := m.accountIdFrom(extrinsic.Signature())
origin := m.originFrom(benchmarkConfig, accountId)

measuredDurations := []int64{}

benchmarking.StoreSnapshotDb()

// Always do at least one internal repeat.
repeats := int(benchmarkConfig.InternalRepeats)
if repeats < 1 {
repeats = 1
}
for i := 1; i <= repeats; i++ {
// The dispatch call is executed in a transactional context,
// allowing to rollback and reset the state after each iteration.
// as an alternative of providing before hook.

benchmarking.RestoreSnapshotDb()

// Does nothing, for now
benchmarking.WipeDb()

// Set up the externalities environment for the setup we want to
// benchmark.

// Sets the block number to 1 to allow emitting events
m.systemModule.StorageBlockNumberSet(1)

// Commit the externalities to the database, flushing the DB cache.
// This will enable worst case scenario for reading from the database.
// Does nothing, for now
benchmarking.CommitDb()

// Whitelist known storage keys.
benchmarking.SetWhitelist([]byte(":transaction_level:"))
benchmarking.SetWhitelist([]byte(":extrinsic_index"))

// Whitelist the signer account key.
keyStorageAccount := m.accountStorageKeyFrom(accountId.Value)
benchmarking.SetWhitelist(keyStorageAccount)

// Reset the read/write counter so we don't count
// operations in the setup process.
benchmarking.ResetReadWriteCount()

benchmarking.StartDbTracker()

var start, end int64

start = benchmarking.CurrentTime()
_, err := m.transactional.WithStorageLayer(
func() (primitives.PostDispatchInfo, error) {
return function.Dispatch(origin, args)
},
)
end = benchmarking.CurrentTime()
if err != nil {
m.logger.Critical(err.Error())
}

// Calculate the diff caused by the benchmark.
measuredDurations = append(measuredDurations, end-start)

benchmarking.StopDbTracker()

// Commit the changes to get proper write count.
// Does nothing, for now
benchmarking.CommitDb()
}

// Calculate the average time.
extrinsicTime := calculateAverageTime(measuredDurations)

benchmarkResult := benchmarking.BenchmarkResult{
ExtrinsicTime: sc.NewU128(extrinsicTime),
Reads: sc.U32(benchmarking.DbReadCount()),
Writes: sc.U32(benchmarking.DbWriteCount()),
}.Bytes()

return m.memUtils.BytesToOffsetAndSize(benchmarkResult)
}

func calculateAverageTime(durations []int64) int64 {
var sum int64
for _, duration := range durations {
sum += duration
}
return sum / int64(len(durations))
}

func (m Module) accountIdFrom(signature sc.Option[primitives.ExtrinsicSignature]) sc.Option[primitives.AccountId] {
var accountId = sc.NewOption[primitives.AccountId](nil)
if signature.HasValue {
id, err := signature.Value.Signer.AsAccountId()
if err != nil {
m.logger.Critical(err.Error())
}
accountId.Value = id
}

return accountId
}

func (m Module) originFrom(benchmarkConfig benchmarking.BenchmarkConfig, accountId sc.Option[primitives.AccountId]) primitives.RawOrigin {
if benchmarkConfig.Origin.HasValue {
return benchmarkConfig.Origin.Value
} else {
return primitives.RawOriginFrom(accountId)
}
}

func (m Module) accountStorageKeyFrom(address primitives.AccountId) []byte {
// TODO:
// reuse already implemented storage keys generation ?
//
// support.NewHashStorageValue[primitives.AccountId](
// []byte("System"),
// []byte("Account"),
// primitives.DecodeAccountId,
// )
addressBytes := address.FixedSequence.Bytes()
keySystemHash := m.hashing.Twox128([]byte("System"))
keyAccountHash := m.hashing.Twox128([]byte("Account"))
addressHash := m.hashing.Blake128(addressBytes)
keyStorageAccount := append(keySystemHash, keyAccountHash...)
keyStorageAccount = append(keyStorageAccount, addressHash...)
keyStorageAccount = append(keyStorageAccount, addressBytes...)
return keyStorageAccount
}
Binary file modified build/runtime.wasm
Binary file not shown.
47 changes: 47 additions & 0 deletions env/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//go:build !benchmarks

package env

func ExtBenchmarkingCurrentTimeVersion1() int64 {
panic("not implemented")
}

func ExtBenchmarkingSetWhitelistVersion1(key int64) {
panic("not implemented")
}

func ExtBenchmarkingResetReadWriteCountVersion1() {
panic("not implemented")
}

func ExtBenchmarkingStartDbTrackerVersion1() {
panic("not implemented")
}

func ExtBenchmarkingStopDbTrackerVersion1() {
panic("not implemented")
}

func ExtBenchmarkingWipeDbVersion1() {
panic("not implemented")
}

func ExtBenchmarkingCommitDbVersion1() {
panic("not implemented")
}

func ExtBenchmarkingDbReadCountVersion1() int32 {
panic("not implemented")
}

func ExtBenchmarkingDbWriteCountVersion1() int32 {
panic("not implemented")
}

func ExtBenchmarkingStoreSnapshotDbVersion1() {
panic("not implemented")
}

func ExtBenchmarkingRestoreSnapshotDbVersion1() {
panic("not implemented")
}
36 changes: 36 additions & 0 deletions env/utils_benchmarks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//go:build benchmarks

package env

//go:wasmimport env ext_benchmarking_current_time_version_1
func ExtBenchmarkingCurrentTimeVersion1() int64

//go:wasmimport env ext_benchmarking_set_whitelist_version_1
func ExtBenchmarkingSetWhitelistVersion1(key int64)

//go:wasmimport env ext_benchmarking_reset_read_write_count_version_1
func ExtBenchmarkingResetReadWriteCountVersion1()

//go:wasmimport env ext_benchmarking_start_db_tracker_version_1
func ExtBenchmarkingStartDbTrackerVersion1()

//go:wasmimport env ext_benchmarking_stop_db_tracker_version_1
func ExtBenchmarkingStopDbTrackerVersion1()

//go:wasmimport env ext_benchmarking_wipe_db_version_1
func ExtBenchmarkingWipeDbVersion1()

//go:wasmimport env ext_benchmarking_commit_db_version_1
func ExtBenchmarkingCommitDbVersion1()

//go:wasmimport env ext_benchmarking_db_read_count_version_1
func ExtBenchmarkingDbReadCountVersion1() int32

//go:wasmimport env ext_benchmarking_db_write_count_version_1
func ExtBenchmarkingDbWriteCountVersion1() int32

//go:wasmimport env ext_benchmarking_store_snapshot_db_version_1
func ExtBenchmarkingStoreSnapshotDbVersion1()

//go:wasmimport env ext_benchmarking_restore_snapshot_db_version_1
func ExtBenchmarkingRestoreSnapshotDbVersion1()
1 change: 1 addition & 0 deletions frame/system/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Module interface {
StorageBlockHashExists(key sc.U64) bool

StorageBlockNumber() (sc.U64, error)
StorageBlockNumberSet(sc.U64)

StorageLastRuntimeUpgrade() (types.LastRuntimeUpgradeInfo, error)
StorageLastRuntimeUpgradeSet(lrui types.LastRuntimeUpgradeInfo)
Expand Down
Loading

0 comments on commit 768722f

Please sign in to comment.