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

VKT: Optimizations #147

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/cespare/cp v0.1.0
github.com/cloudflare/cloudflare-go v0.14.0
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f
github.com/crate-crypto/go-ipa v0.0.0-20221111143132-9aa5d42120bc
github.com/crate-crypto/go-ipa v0.0.0-20230112133036-98736f2f2808
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.8.0
github.com/docker/docker v1.6.2
Expand All @@ -23,7 +23,7 @@ require (
github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
github.com/gballet/go-verkle v0.0.0-20221129125207-513116151b28
github.com/gballet/go-verkle v0.0.0-20230112140635-3e8bae599d5c
github.com/go-stack/stack v1.8.0
github.com/golang-jwt/jwt/v4 v4.3.0
github.com/golang/protobuf v1.5.2
Expand Down Expand Up @@ -106,3 +106,5 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/gballet/go-verkle => github.com/gballet/go-verkle v0.0.0-20230112140635-3e8bae599d5c
11 changes: 4 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-ipa v0.0.0-20221111143132-9aa5d42120bc h1:mtR7MuscVeP/s0/ERWA2uSr5QOrRYy1pdvZqG1USfXI=
github.com/crate-crypto/go-ipa v0.0.0-20221111143132-9aa5d42120bc/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI=
github.com/crate-crypto/go-ipa v0.0.0-20230112133036-98736f2f2808 h1:zzhDwbi4il5p8+g4WjfX4nWaIo9A61bBS6Ltj8AkExg=
github.com/crate-crypto/go-ipa v0.0.0-20230112133036-98736f2f2808/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
Expand Down Expand Up @@ -135,10 +135,8 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgx
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/gballet/go-verkle v0.0.0-20221122140954-75ceda26b7db h1:YvtZfE11QEYWPjsQCyZLoZCGMsxJs9mTEbhF3MnM32Q=
github.com/gballet/go-verkle v0.0.0-20221122140954-75ceda26b7db/go.mod h1:DMDd04jjQgdynaAwbEgiRERIGpC8fDjx0+y06an7Psg=
github.com/gballet/go-verkle v0.0.0-20221129125207-513116151b28 h1:UbB7D2R1OQCkNFX+LYoo2pHZ0u5LhwR9ldUsY4ZbZqI=
github.com/gballet/go-verkle v0.0.0-20221129125207-513116151b28/go.mod h1:DMDd04jjQgdynaAwbEgiRERIGpC8fDjx0+y06an7Psg=
github.com/gballet/go-verkle v0.0.0-20230112140635-3e8bae599d5c h1:ROHmmjPpri0ApcrUhpXZBsWaeFuyuVEDhrPK1dIsAqA=
github.com/gballet/go-verkle v0.0.0-20230112140635-3e8bae599d5c/go.mod h1:hspihFYcpgSaKoVnQV+97jkJREFZdH/kiPTcBwIHCyM=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
Expand Down Expand Up @@ -554,7 +552,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
76 changes: 76 additions & 0 deletions tests/tries_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package tests
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we have the benchmarks from #146.
I only removed the statelessness one as we discussed, so we avoid test flags.


import (
"fmt"
"math/big"
"math/rand"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/trie"
"github.com/gballet/go-verkle"
)

func BenchmarkTriesRandom(b *testing.B) {
numAccounts := []int{1_000, 5_000, 10_000}

for _, numAccounts := range numAccounts {
rs := rand.New(rand.NewSource(42))
accounts := getRandomStateAccounts(rs, numAccounts)

b.Run(fmt.Sprintf("MPT/%d accounts", numAccounts), func(b *testing.B) {
trie, _ := trie.NewStateTrie(trie.TrieID(common.Hash{}), trie.NewDatabase(memorydb.New()))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for k := 0; k < len(accounts); k++ {
trie.TryUpdateAccount(accounts[k].address[:], &accounts[k].stateAccount)
}
trie.Commit(true)
}
})
b.Run(fmt.Sprintf("VKT/%d accounts", numAccounts), func(b *testing.B) {
// Warmup VKT configuration
trie.NewVerkleTrie(verkle.New(), trie.NewDatabase(memorydb.New())).TryUpdate([]byte("00000000000000000000000000000012"), []byte("B"))

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
trie := trie.NewVerkleTrie(verkle.New(), trie.NewDatabase(memorydb.New()))
for k := 0; k < len(accounts); k++ {
trie.TryUpdateAccount(accounts[k].address[:], &accounts[k].stateAccount)
}
trie.Commit(false)
}
})
}
}

type randomAccount struct {
address common.Address
stateAccount types.StateAccount
}

func getRandomStateAccounts(rand *rand.Rand, count int) []randomAccount {
randomBytes := func(size int) []byte {
ret := make([]byte, size)
rand.Read(ret)
return ret
}

accounts := make([]randomAccount, count)
for i := range accounts {
accounts[i] = randomAccount{
address: common.BytesToAddress(randomBytes(common.AddressLength)),
stateAccount: types.StateAccount{
Nonce: rand.Uint64(),
Balance: big.NewInt(int64(rand.Uint64())),
Root: common.Hash{},
CodeHash: nil,
},
}
}
return accounts
}
8 changes: 5 additions & 3 deletions trie/utils/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
// 32-byte aligned big-endian representation (BE({00,...,AA,BB,CC})).
// - poly[4]'s byte representation is the same as the *low* 16 bytes (trieIndexBytes[:16]) of
// the 32-byte aligned big-endian representation (BE({00,00,...}).
trieIndexBytes := treeIndex.Bytes32()
verkle.FromBytes(&poly[3], trieIndexBytes[16:])
verkle.FromBytes(&poly[4], trieIndexBytes[:16])
if !treeIndex.IsZero() {
trieIndexBytes := treeIndex.Bytes32()
verkle.FromBytes(&poly[3], trieIndexBytes[16:])
verkle.FromBytes(&poly[4], trieIndexBytes[:16])
}
Comment on lines -85 to +89
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First optimization is avoiding work if treeIndex is zero, which makes sense and is a pretty normal case.


cfg := verkle.GetConfig()
ret := cfg.CommitToPoly(poly[:], 0)
Expand Down
33 changes: 21 additions & 12 deletions trie/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (t *VerkleTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error
var (
err error
nonce, balance [32]byte
values = make([][]byte, verkle.NodeWidth)
values = make([][]byte, 256)
stem = utils.GetTreeKeyVersion(key[:])
)

Expand Down Expand Up @@ -156,10 +156,9 @@ func (t *VerkleTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error
}

func (trie *VerkleTrie) TryUpdateStem(key []byte, values [][]byte) {
resolver :=
func(h []byte) ([]byte, error) {
return trie.db.diskdb.Get(h)
}
resolver := func(h []byte) ([]byte, error) {
return trie.db.diskdb.Get(h)
}
Comment on lines -159 to +161
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lint.

switch root := trie.root.(type) {
case *verkle.InternalNode:
root.InsertStem(key, values, resolver)
Expand Down Expand Up @@ -239,9 +238,23 @@ func nodeToDBKey(n verkle.VerkleNode) []byte {
// Commit writes all nodes to the trie's memory database, tracking the internal
// and external (for account tries) references.
func (trie *VerkleTrie) Commit(_ bool) (common.Hash, *NodeSet, error) {
flush := make(chan verkle.VerkleNode)
type vnflush struct {
n verkle.VerkleNode
value []byte
dbKey []byte
}
flush := make(chan vnflush, 1024)
resolver := func(n verkle.VerkleNode) {
flush <- n
value, err := n.Serialize()
if err != nil {
panic(err)
}
dbKey := nodeToDBKey(n)
flush <- vnflush{
n: n,
value: value,
dbKey: dbKey,
}
Comment on lines -242 to +257
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking further at where the wall-clock time was going, I realized that after Commit()ing the underlying go-verkle trie, a significant amount of time was spent flushing the result.

What I did in ethereum/go-verkle#314, is making Flush() for the root node to do the work in parallel. This means that resolver will be called by multiple goroutines balancing more CPU work in all available cores.

To squeeze things more, the Serialize() part of the process was now moved from L258 to the resolver. This means that the Serialize() work will also be done in the goroutines that are flushing the result, and not in a single core.

In a nutshell, since the resolver execution is exploiting all cores, we want to do as much work as possible there, so the main goroutine ranging L269 is receiving plain results to be stored in the diskdb. This main goroutine isn't doing heavy CPU work, just receiving exactly what needs to be stored.

This is important, since if the range in L269 is slow, it will slow down everything. Note how I also made a buffered channel in L246. We need to avoid as much as possible to be blocking goroutines, so having extra breathing room also improved performance.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if #295 gets merged, Serialize will become a tad more expensive - at the cost of a cheaper deserialization (hence not producing a tree that is crash-prone because of potentially invalid commitments, as in your current approach). Your change, I believe, will mitigate this problem and make it cheaper to follow this approach. That's very nice.

}
go func() {
switch root := trie.root.(type) {
Expand All @@ -255,12 +268,8 @@ func (trie *VerkleTrie) Commit(_ bool) (common.Hash, *NodeSet, error) {
var commitCount int
for n := range flush {
commitCount += 1
value, err := n.Serialize()
if err != nil {
panic(err)
}

if err := trie.db.diskdb.Put(nodeToDBKey(n), value); err != nil {
if err := trie.db.diskdb.Put(n.dbKey, n.value); err != nil {
return common.Hash{}, NewNodeSet(common.Hash{}), err
}
}
Expand Down