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

Faster trie node encode #533

Merged
merged 2 commits into from
Jun 20, 2022
Merged
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
16 changes: 0 additions & 16 deletions lowrlp/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package lowrlp

import (
"io"
"sync"
)

// Encoder is the low-level rlp encoder.
Expand All @@ -20,28 +19,13 @@ type Encoder struct {
sizebuf [9]byte // auxiliary buffer for uint encoding
}

var pool = sync.Pool{
New: func() interface{} { return &Encoder{} },
}

func NewEncoder() *Encoder {
w := pool.Get().(*Encoder)
w.Reset()
return w
}

// Reset reset the encoder state.
func (w *Encoder) Reset() {
w.lhsize = 0
w.str = w.str[:0]
w.lheads = w.lheads[:0]
}

// Release puts back the inner state into pool.
func (w *Encoder) Release() {
pool.Put(w)
}

// EncodeString encodes the string value.
func (w *Encoder) EncodeString(b []byte) {
if len(b) == 1 && b[0] <= 0x7F {
Expand Down
3 changes: 1 addition & 2 deletions state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,13 +462,12 @@ func (s *State) Stage(newBlockNum, newBlockConflicts uint32) (*Stage, error) {
c.storage[key.key] = v.(rlp.RawValue)
if len(c.meta.StorageID) == 0 {
// generate storage id for the new storage trie.
enc := lowrlp.NewEncoder()
var enc lowrlp.Encoder
enc.EncodeUint(uint64(newBlockNum))
enc.EncodeUint(uint64(newBlockConflicts))
enc.EncodeUint(uint64(storageTrieCreationCount))
storageTrieCreationCount++
c.meta.StorageID = enc.ToBytes()
enc.Release()
}
case storageBarrierKey:
if c, jerr = getChanged(thor.Address(key)); jerr != nil {
Expand Down
113 changes: 46 additions & 67 deletions trie/fast_node_encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,87 +6,66 @@
package trie

import (
"io"

"github.com/vechain/thor/lowrlp"
)

// fastNodeEncoder is the fast node encoder using low-level rlp encoder.
type fastNodeEncoder struct{}
// implements node.encode and node.encodeTrailing

var frlp fastNodeEncoder
func (n *fullNode) encode(e *lowrlp.Encoder, nonCrypto bool) {
off := e.List()
for _, c := range n.Children {
if c != nil {
c.encode(e, nonCrypto)
} else {
e.EncodeEmptyString()
}
}
e.ListEnd(off)
}

// Encode writes the RLP encoding of node to w.
func (fastNodeEncoder) Encode(w io.Writer, node node, nonCrypto bool) error {
enc := lowrlp.NewEncoder()
defer enc.Release()
func (n *fullNode) encodeTrailing(e *lowrlp.Encoder) {
for _, c := range n.Children {
if c != nil {
c.encodeTrailing(e)
}
}
}

fastEncodeNode(enc, node, nonCrypto)
return enc.ToWriter(w)
func (n *shortNode) encode(e *lowrlp.Encoder, nonCrypto bool) {
off := e.List()
e.EncodeString(n.Key)
if n.Val != nil {
n.Val.encode(e, nonCrypto)
} else {
e.EncodeEmptyString()
}
e.ListEnd(off)
}

// EncodeToBytes returns the RLP encoding of node.
func (fastNodeEncoder) EncodeToBytes(collapsed node, nonCrypto bool) []byte {
enc := lowrlp.NewEncoder()
defer enc.Release()
func (n *shortNode) encodeTrailing(e *lowrlp.Encoder) {
if n.Val != nil {
n.Val.encodeTrailing(e)
}
}

fastEncodeNode(enc, collapsed, nonCrypto)
return enc.ToBytes()
func (n *hashNode) encode(e *lowrlp.Encoder, nonCrypto bool) {
if nonCrypto {
e.EncodeString(nonCryptoNodeHashPlaceholder)
} else {
e.EncodeString(n.Hash[:])
}
}

func (fastNodeEncoder) EncodeTrailing(w io.Writer, collapsed node) error {
enc := lowrlp.NewEncoder()
defer enc.Release()
fastEncodeNodeTrailing(enc, collapsed)
return enc.ToWriter(w)
func (n *hashNode) encodeTrailing(e *lowrlp.Encoder) {
e.EncodeUint(n.seq)
}

func fastEncodeNode(w *lowrlp.Encoder, collapsed node, nonCrypto bool) {
switch n := collapsed.(type) {
case *fullNode:
offset := w.List()
for _, c := range n.Children {
if c != nil {
fastEncodeNode(w, c, nonCrypto)
} else {
w.EncodeEmptyString()
}
}
w.ListEnd(offset)
case *shortNode:
offset := w.List()
w.EncodeString(n.Key)
if n.Val != nil {
fastEncodeNode(w, n.Val, nonCrypto)
} else {
w.EncodeEmptyString()
}
w.ListEnd(offset)
case *hashNode:
if nonCrypto {
w.EncodeString(nonCryptoNodeHashPlaceholder)
} else {
w.EncodeString(n.Hash[:])
}
case *valueNode:
w.EncodeString(n.Value)
}
func (n *valueNode) encode(e *lowrlp.Encoder, nonCrypto bool) {
e.EncodeString(n.Value)
}

func fastEncodeNodeTrailing(w *lowrlp.Encoder, collapsed node) {
switch n := collapsed.(type) {
case *shortNode:
fastEncodeNodeTrailing(w, n.Val)
case *fullNode:
for _, c := range n.Children {
fastEncodeNodeTrailing(w, c)
}
case *valueNode:
// skip empty node
if len(n.Value) > 0 {
w.EncodeString(n.meta)
}
case *hashNode:
w.EncodeUint(n.seq)
func (n *valueNode) encodeTrailing(e *lowrlp.Encoder) {
if len(n.Value) > 0 {
e.EncodeString(n.meta)
}
}
14 changes: 8 additions & 6 deletions trie/hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import (
"sync"

"github.com/ethereum/go-ethereum/common"
"github.com/vechain/thor/lowrlp"
"github.com/vechain/thor/thor"
)

type hasher struct {
enc lowrlp.Encoder
tmp sliceBuffer
cacheGen uint16
cacheTTL uint16
Expand Down Expand Up @@ -188,10 +190,10 @@ func (h *hasher) store(n node, db DatabaseWriter, path []byte, force bool) (node
return n, nil
}
// Generate the RLP encoding of the node
h.enc.Reset()
n.encode(&h.enc, h.nonCrypto)
h.tmp.Reset()
if err := frlp.Encode(&h.tmp, n, h.nonCrypto); err != nil {
panic("encode error: " + err.Error())
}
h.enc.ToWriter(&h.tmp)

if h.nonCrypto {
// fullnode and shortnode with non-value child are forced
Expand Down Expand Up @@ -225,9 +227,9 @@ func (h *hasher) store(n node, db DatabaseWriter, path []byte, force bool) (node
if db != nil {
// extended
if h.extended {
if err := frlp.EncodeTrailing(&h.tmp, n); err != nil {
return nil, err
}
h.enc.Reset()
n.encodeTrailing(&h.enc)
h.enc.ToWriter(&h.tmp)
hash.seq = h.seq
}

Expand Down
14 changes: 8 additions & 6 deletions trie/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,13 @@ func (it *nodeIterator) Node(handler func(blob []byte) error) error {

collapsed, _, _ := h.hashChildren(st.node, nil, it.path)

h.tmp.Reset()
_ = frlp.Encode(&h.tmp, collapsed, it.nonCrypto)

h.enc.Reset()
collapsed.encode(&h.enc, h.nonCrypto)
if it.extended {
_ = frlp.EncodeTrailing(&h.tmp, collapsed)
collapsed.encodeTrailing(&h.enc)
}
h.tmp.Reset()
h.enc.ToWriter(&h.tmp)
return handler(h.tmp)
}

Expand Down Expand Up @@ -239,8 +240,9 @@ func (it *nodeIterator) LeafProof() [][]byte {
node, _, _ := hasher.hashChildren(item.node, nil, nil)
hashed, _ := hasher.store(node, nil, nil, false)
if _, ok := hashed.(*hashNode); ok || i == 0 {
enc := frlp.EncodeToBytes(node, false)
proofs = append(proofs, enc)
hasher.enc.Reset()
node.encode(&hasher.enc, hasher.nonCrypto)
proofs = append(proofs, hasher.enc.ToBytes())
}
}
return proofs
Expand Down
3 changes: 3 additions & 0 deletions trie/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"

"github.com/ethereum/go-ethereum/rlp"
"github.com/vechain/thor/lowrlp"
"github.com/vechain/thor/thor"
)

Expand All @@ -36,6 +37,8 @@ type node interface {
fstring(string) string
cache() (*hashNode, bool, uint16)
seqNum() uint64
encode(e *lowrlp.Encoder, nonCrypto bool)
encodeTrailing(*lowrlp.Encoder)
}

type (
Expand Down
9 changes: 6 additions & 3 deletions trie/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,17 @@ func BenchmarkEncodeFullNode(b *testing.B) {
}

func BenchmarkFastEncodeFullNode(b *testing.B) {
var buf sliceBuffer
f := &fullNode{}
for i := 0; i < len(f.Children); i++ {
f.Children[i] = &hashNode{Hash: thor.BytesToBytes32(randBytes(32))}
}

h := newHasher(0, 0)

for i := 0; i < b.N; i++ {
buf.Reset()
frlp.Encode(&buf, f, false)
h.enc.Reset()
f.encode(&h.enc, false)
h.tmp.Reset()
h.enc.ToWriter(&h.tmp)
}
}
9 changes: 6 additions & 3 deletions trie/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb DatabaseWriter) error {
if fromLevel > 0 {
fromLevel--
} else {
enc := frlp.EncodeToBytes(n, false)
hasher.enc.Reset()
n.encode(&hasher.enc, hasher.nonCrypto)
hasher.tmp.Reset()
hasher.enc.ToWriter(&hasher.tmp)
if ok {
proofDb.Put(hash.Hash[:], enc)
proofDb.Put(hash.Hash[:], hasher.tmp)
} else {
proofDb.Put(thor.Blake2b(enc).Bytes(), enc)
proofDb.Put(thor.Blake2b(hasher.tmp).Bytes(), hasher.tmp)
}
}
}
Expand Down