mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +00:00
trie, core: use unsafe pool internally in stacktrie
This commit is contained in:
parent
e645cdd356
commit
f8412c2953
3 changed files with 55 additions and 15 deletions
|
|
@ -93,10 +93,12 @@ func BenchmarkDeriveSha200(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
st := trie.NewStackTrie(nil)
|
||||||
b.Run("stack_trie", func(b *testing.B) {
|
b.Run("stack_trie", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
have = types.DeriveSha(txs, trie.NewStackTrie(nil))
|
st.Reset()
|
||||||
|
have = types.DeriveSha(txs, st)
|
||||||
}
|
}
|
||||||
if have != want {
|
if have != want {
|
||||||
b.Errorf("have %x want %x", have, want)
|
b.Errorf("have %x want %x", have, want)
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ func newBytesPool(sliceCap, nitems int) *bytesPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns a slice. Safe for concurrent use.
|
// get returns a slice. Safe for concurrent use.
|
||||||
func (bp *bytesPool) Get() []byte {
|
func (bp *bytesPool) get() []byte {
|
||||||
select {
|
select {
|
||||||
case b := <-bp.c:
|
case b := <-bp.c:
|
||||||
return b
|
return b
|
||||||
|
|
@ -42,18 +42,18 @@ func (bp *bytesPool) Get() []byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWithSize returns a slice with specified byte slice size.
|
// getWithSize returns a slice with specified byte slice size.
|
||||||
func (bp *bytesPool) GetWithSize(s int) []byte {
|
func (bp *bytesPool) getWithSize(s int) []byte {
|
||||||
b := bp.Get()
|
b := bp.get()
|
||||||
if cap(b) < s {
|
if cap(b) < s {
|
||||||
return make([]byte, s)
|
return make([]byte, s)
|
||||||
}
|
}
|
||||||
return b[:s]
|
return b[:s]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put returns a slice to the pool. Safe for concurrent use. This method
|
// put returns a slice to the pool. Safe for concurrent use. This method
|
||||||
// will ignore slices that are too small or too large (>3x the cap)
|
// will ignore slices that are too small or too large (>3x the cap)
|
||||||
func (bp *bytesPool) Put(b []byte) {
|
func (bp *bytesPool) put(b []byte) {
|
||||||
if c := cap(b); c < bp.w || c > 3*bp.w {
|
if c := cap(b); c < bp.w || c > 3*bp.w {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -62,3 +62,40 @@ func (bp *bytesPool) Put(b []byte) {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unsafeBytesPool is a pool for byte slices. It is not safe for concurrent use.
|
||||||
|
type unsafeBytesPool struct {
|
||||||
|
items [][]byte
|
||||||
|
w int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUnsafeBytesPool creates a new unsafeBytesPool. The sliceCap sets the
|
||||||
|
// capacity of newly allocated slices, and the nitems determines how many
|
||||||
|
// items the pool will hold, at maximum.
|
||||||
|
func newUnsafeBytesPool(sliceCap, nitems int) *unsafeBytesPool {
|
||||||
|
return &unsafeBytesPool{
|
||||||
|
items: make([][]byte, 0, nitems),
|
||||||
|
w: sliceCap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a slice.
|
||||||
|
func (bp *unsafeBytesPool) get() []byte {
|
||||||
|
if len(bp.items) > 0 {
|
||||||
|
last := bp.items[len(bp.items)-1]
|
||||||
|
bp.items = bp.items[:len(bp.items)-1]
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
return make([]byte, 0, bp.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// put returns a slice to the pool. This method
|
||||||
|
// will ignore slices that are too small or too large (>3x the cap)
|
||||||
|
func (bp *unsafeBytesPool) put(b []byte) {
|
||||||
|
if c := cap(b); c < bp.w || c > 3*bp.w {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bp.items) < cap(bp.items) {
|
||||||
|
bp.items = append(bp.items, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ type StackTrie struct {
|
||||||
onTrieNode OnTrieNode
|
onTrieNode OnTrieNode
|
||||||
kBuf []byte // buf space used for hex-key during insertions
|
kBuf []byte // buf space used for hex-key during insertions
|
||||||
pBuf []byte // buf space used for path during insertions
|
pBuf []byte // buf space used for path during insertions
|
||||||
vPool *bytesPool
|
vPool *unsafeBytesPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStackTrie allocates and initializes an empty trie. The committed nodes
|
// NewStackTrie allocates and initializes an empty trie. The committed nodes
|
||||||
|
|
@ -62,7 +62,7 @@ func NewStackTrie(onTrieNode OnTrieNode) *StackTrie {
|
||||||
onTrieNode: onTrieNode,
|
onTrieNode: onTrieNode,
|
||||||
kBuf: make([]byte, 64),
|
kBuf: make([]byte, 64),
|
||||||
pBuf: make([]byte, 64),
|
pBuf: make([]byte, 64),
|
||||||
vPool: newBytesPool(300, 20),
|
vPool: newUnsafeBytesPool(300, 20),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ func (t *StackTrie) Update(key, value []byte) error {
|
||||||
} else {
|
} else {
|
||||||
t.last = append(t.last[:0], k...) // reuse key slice
|
t.last = append(t.last[:0], k...) // reuse key slice
|
||||||
}
|
}
|
||||||
vBuf := t.vPool.Get()
|
vBuf := t.vPool.get()
|
||||||
if cap(vBuf) < len(value) {
|
if cap(vBuf) < len(value) {
|
||||||
vBuf = common.CopyBytes(value)
|
vBuf = common.CopyBytes(value)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -114,6 +114,7 @@ func (t *StackTrie) Update(key, value []byte) error {
|
||||||
func (t *StackTrie) Reset() {
|
func (t *StackTrie) Reset() {
|
||||||
t.root = stPool.Get().(*stNode)
|
t.root = stPool.Get().(*stNode)
|
||||||
t.last = nil
|
t.last = nil
|
||||||
|
t.onTrieNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrieKey returns the internal key representation for the given user key.
|
// TrieKey returns the internal key representation for the given user key.
|
||||||
|
|
@ -166,7 +167,7 @@ func (n *stNode) reset() *stNode {
|
||||||
// On hashnodes, we 'own' the val: it is guaranteed to be not held
|
// On hashnodes, we 'own' the val: it is guaranteed to be not held
|
||||||
// by external caller. Hence, when we arrive here, we can put it back
|
// by external caller. Hence, when we arrive here, we can put it back
|
||||||
// into the pool
|
// into the pool
|
||||||
bPool.Put(n.val)
|
bPool.put(n.val)
|
||||||
}
|
}
|
||||||
n.key = n.key[:0]
|
n.key = n.key[:0]
|
||||||
n.val = nil
|
n.val = nil
|
||||||
|
|
@ -413,20 +414,20 @@ func (t *StackTrie) hash(st *stNode, path []byte) {
|
||||||
|
|
||||||
// Release reference to (potentially externally held) value-slice.
|
// Release reference to (potentially externally held) value-slice.
|
||||||
if cap(st.val) > 0 && t.vPool != nil {
|
if cap(st.val) > 0 && t.vPool != nil {
|
||||||
t.vPool.Put(st.val)
|
t.vPool.put(st.val)
|
||||||
}
|
}
|
||||||
st.val = nil
|
st.val = nil
|
||||||
|
|
||||||
// Skip committing the non-root node if the size is smaller than 32 bytes
|
// Skip committing the non-root node if the size is smaller than 32 bytes
|
||||||
// as tiny nodes are always embedded in their parent except root node.
|
// as tiny nodes are always embedded in their parent except root node.
|
||||||
if len(blob) < 32 && len(path) > 0 {
|
if len(blob) < 32 && len(path) > 0 {
|
||||||
st.val = bPool.GetWithSize(len(blob))
|
st.val = bPool.getWithSize(len(blob))
|
||||||
copy(st.val, blob)
|
copy(st.val, blob)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
||||||
// input values.
|
// input values.
|
||||||
st.val = bPool.GetWithSize(32)
|
st.val = bPool.getWithSize(32)
|
||||||
t.h.hashDataTo(st.val, blob)
|
t.h.hashDataTo(st.val, blob)
|
||||||
|
|
||||||
// Invoke the callback it's provided. Notably, the path and blob slices are
|
// Invoke the callback it's provided. Notably, the path and blob slices are
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue