From 922a658703da075e2e85f40e751b2a343fd4dc2e Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Wed, 20 Jul 2022 12:45:48 +0200 Subject: [PATCH 1/6] perf: use fast unsafe bytes->string convertion --- cmd/iaviewer/main.go | 3 +- internal/bytes/bytes.go | 6 ++-- internal/bytes/string.go | 26 +++++++++++++++++ internal/bytes/string_test.go | 54 +++++++++++++++++++++++++++++++++++ mutable_tree.go | 20 +++++++------ nodedb.go | 28 ++++++++++-------- tree_dotgraph.go | 6 ++-- unsaved_fast_iterator.go | 40 +++++++++++--------------- 8 files changed, 133 insertions(+), 50 deletions(-) create mode 100644 internal/bytes/string.go create mode 100644 internal/bytes/string_test.go diff --git a/cmd/iaviewer/main.go b/cmd/iaviewer/main.go index ee881d9ea..476811598 100644 --- a/cmd/iaviewer/main.go +++ b/cmd/iaviewer/main.go @@ -12,6 +12,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/iavl" + ibytes "github.com/cosmos/iavl/internal/bytes" ) // TODO: make this configurable? @@ -94,7 +95,7 @@ func PrintDBStats(db dbm.DB) { defer itr.Close() for ; itr.Valid(); itr.Next() { - key := string(itr.Key()[:1]) + key := ibytes.UnsafeBytesToStr(itr.Key()[:1]) prefix[key]++ count++ } diff --git a/internal/bytes/bytes.go b/internal/bytes/bytes.go index 310c82eb0..ac3197d8a 100644 --- a/internal/bytes/bytes.go +++ b/internal/bytes/bytes.go @@ -35,11 +35,13 @@ func (bz *HexBytes) UnmarshalJSON(data []byte) error { if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { return fmt.Errorf("invalid hex string: %s", data) } - bz2, err := hex.DecodeString(string(data[1 : len(data)-1])) + data = data[1 : len(data)-1] + dest := make([]byte, hex.DecodedLen(len(data))) + _, err := hex.Decode(dest, data) if err != nil { return err } - *bz = bz2 + *bz = dest return nil } diff --git a/internal/bytes/string.go b/internal/bytes/string.go new file mode 100644 index 000000000..a09422218 --- /dev/null +++ b/internal/bytes/string.go @@ -0,0 +1,26 @@ +package common + +import ( + "reflect" + "unsafe" +) + +// UnsafeStrToBytes uses unsafe to convert string into byte array. Returned bytes +// must not be altered after this function is called as it will cause a segmentation fault. +func UnsafeStrToBytes(s string) []byte { + var buf []byte + sHdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) + bufHdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + bufHdr.Data = sHdr.Data + bufHdr.Cap = sHdr.Len + bufHdr.Len = sHdr.Len + return buf +} + +// UnsafeBytesToStr is meant to make a zero allocation conversion +// from []byte -> string to speed up operations, it is not meant +// to be used generally, but for a specific pattern to delete keys +// from a map. +func UnsafeBytesToStr(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/internal/bytes/string_test.go b/internal/bytes/string_test.go new file mode 100644 index 000000000..f704e2f85 --- /dev/null +++ b/internal/bytes/string_test.go @@ -0,0 +1,54 @@ +package common + +import ( + "runtime" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/suite" +) + +func TestStringSuite(t *testing.T) { + suite.Run(t, new(StringSuite)) +} + +type StringSuite struct{ suite.Suite } + +func unsafeConvertStr() []byte { + return UnsafeStrToBytes("abc") +} + +func (s *StringSuite) TestUnsafeStrToBytes() { + // we convert in other function to trigger GC. We want to check that + // the underlying array in []bytes is accessible after GC will finish swapping. + for i := 0; i < 5; i++ { + b := unsafeConvertStr() + runtime.GC() + <-time.NewTimer(2 * time.Millisecond).C + b2 := append(b, 'd') + s.Equal("abc", string(b)) + s.Equal("abcd", string(b2)) + } +} + +func unsafeConvertBytes() string { + return UnsafeBytesToStr([]byte("abc")) +} + +func (s *StringSuite) TestUnsafeBytesToStr() { + // we convert in other function to trigger GC. We want to check that + // the underlying array in []bytes is accessible after GC will finish swapping. + for i := 0; i < 5; i++ { + str := unsafeConvertBytes() + runtime.GC() + <-time.NewTimer(2 * time.Millisecond).C + s.Equal("abc", str) + } +} + +func BenchmarkUnsafeStrToBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + UnsafeStrToBytes(strconv.Itoa(i)) + } +} diff --git a/mutable_tree.go b/mutable_tree.go index 654b1347f..c30984ef9 100644 --- a/mutable_tree.go +++ b/mutable_tree.go @@ -9,11 +9,11 @@ import ( "sync" "time" + "github.com/cosmos/iavl/internal/logger" "github.com/pkg/errors" - dbm "github.com/tendermint/tm-db" - "github.com/cosmos/iavl/internal/logger" + ibytes "github.com/cosmos/iavl/internal/bytes" ) // ErrVersionDoesNotExist is returned if a requested version does not exist. @@ -147,7 +147,7 @@ func (tree *MutableTree) Get(key []byte) ([]byte, error) { return nil, nil } - if fastNode, ok := tree.unsavedFastNodeAdditions[string(key)]; ok { + if fastNode, ok := tree.unsavedFastNodeAdditions[ibytes.UnsafeBytesToStr(key)]; ok { return fastNode.value, nil } @@ -916,8 +916,9 @@ func (tree *MutableTree) getUnsavedFastNodeRemovals() map[string]interface{} { } func (tree *MutableTree) addUnsavedAddition(key []byte, node *FastNode) { - delete(tree.unsavedFastNodeRemovals, string(key)) - tree.unsavedFastNodeAdditions[string(key)] = node + skey := ibytes.UnsafeBytesToStr(key) + delete(tree.unsavedFastNodeRemovals, skey) + tree.unsavedFastNodeAdditions[skey] = node } func (tree *MutableTree) saveFastNodeAdditions() error { @@ -936,8 +937,9 @@ func (tree *MutableTree) saveFastNodeAdditions() error { } func (tree *MutableTree) addUnsavedRemoval(key []byte) { - delete(tree.unsavedFastNodeAdditions, string(key)) - tree.unsavedFastNodeRemovals[string(key)] = true + skey := ibytes.UnsafeBytesToStr(key) + delete(tree.unsavedFastNodeAdditions, skey) + tree.unsavedFastNodeRemovals[skey] = true } func (tree *MutableTree) saveFastNodeRemovals() error { @@ -948,7 +950,7 @@ func (tree *MutableTree) saveFastNodeRemovals() error { sort.Strings(keysToSort) for _, key := range keysToSort { - if err := tree.ndb.DeleteFastNode([]byte(key)); err != nil { + if err := tree.ndb.DeleteFastNode(ibytes.UnsafeStrToBytes(key)); err != nil { return err } } @@ -1228,7 +1230,7 @@ func (tree *MutableTree) addOrphans(orphans []*Node) error { if len(node.hash) == 0 { return fmt.Errorf("expected to find node hash, but was empty") } - tree.orphans[string(node.hash)] = node.version + tree.orphans[ibytes.UnsafeBytesToStr(node.hash)] = node.version } return nil } diff --git a/nodedb.go b/nodedb.go index f986bbbd6..e41439346 100644 --- a/nodedb.go +++ b/nodedb.go @@ -11,9 +11,11 @@ import ( "strings" "sync" - "github.com/cosmos/iavl/internal/logger" "github.com/pkg/errors" dbm "github.com/tendermint/tm-db" + + ibytes "github.com/cosmos/iavl/internal/bytes" + "github.com/cosmos/iavl/internal/logger" ) const ( @@ -91,7 +93,7 @@ func newNodeDB(db dbm.DB, cacheSize int, opts *Options) *nodeDB { opts = &o } - storeVersion, err := db.Get(metadataKeyFormat.Key([]byte(storageVersionKey))) + storeVersion, err := db.Get(metadataKeyFormat.Key(ibytes.UnsafeStrToBytes(storageVersionKey))) if err != nil || storeVersion == nil { storeVersion = []byte(defaultStorageVersionValue) @@ -124,7 +126,7 @@ func (ndb *nodeDB) GetNode(hash []byte) (*Node, error) { } // Check the cache. - if elem, ok := ndb.nodeCache[string(hash)]; ok { + if elem, ok := ndb.nodeCache[ibytes.UnsafeBytesToStr(hash)]; ok { if ndb.opts.Stat != nil { ndb.opts.Stat.IncCacheHitCnt() } @@ -171,7 +173,7 @@ func (ndb *nodeDB) GetFastNode(key []byte) (*FastNode, error) { } // Check the cache. - if elem, ok := ndb.fastNodeCache[string(key)]; ok { + if elem, ok := ndb.fastNodeCache[ibytes.UnsafeBytesToStr(key)]; ok { if ndb.opts.Stat != nil { ndb.opts.Stat.IncFastCacheHitCnt() } @@ -885,9 +887,10 @@ func (ndb *nodeDB) getFastIterator(start, end []byte, ascending bool) (dbm.Itera } func (ndb *nodeDB) uncacheNode(hash []byte) { - if elem, ok := ndb.nodeCache[string(hash)]; ok { + key := ibytes.UnsafeBytesToStr(hash) + if elem, ok := ndb.nodeCache[key]; ok { ndb.nodeCacheQueue.Remove(elem) - delete(ndb.nodeCache, string(hash)) + delete(ndb.nodeCache, key) } } @@ -895,19 +898,20 @@ func (ndb *nodeDB) uncacheNode(hash []byte) { // reached the cache size limit. func (ndb *nodeDB) cacheNode(node *Node) { elem := ndb.nodeCacheQueue.PushBack(node) - ndb.nodeCache[string(node.hash)] = elem + ndb.nodeCache[ibytes.UnsafeBytesToStr(node.hash)] = elem if ndb.nodeCacheQueue.Len() > ndb.nodeCacheSize { oldest := ndb.nodeCacheQueue.Front() hash := ndb.nodeCacheQueue.Remove(oldest).(*Node).hash - delete(ndb.nodeCache, string(hash)) + delete(ndb.nodeCache, ibytes.UnsafeBytesToStr(hash)) } } func (ndb *nodeDB) uncacheFastNode(key []byte) { - if elem, ok := ndb.fastNodeCache[string(key)]; ok { + skey := ibytes.UnsafeBytesToStr(key) + if elem, ok := ndb.fastNodeCache[skey]; ok { ndb.fastNodeCacheQueue.Remove(elem) - delete(ndb.fastNodeCache, string(key)) + delete(ndb.fastNodeCache, skey) } } @@ -915,12 +919,12 @@ func (ndb *nodeDB) uncacheFastNode(key []byte) { // reached the cache size limit. func (ndb *nodeDB) cacheFastNode(node *FastNode) { elem := ndb.fastNodeCacheQueue.PushBack(node) - ndb.fastNodeCache[string(node.key)] = elem + ndb.fastNodeCache[ibytes.UnsafeBytesToStr(node.key)] = elem if ndb.fastNodeCacheQueue.Len() > ndb.fastNodeCacheSize { oldest := ndb.fastNodeCacheQueue.Front() key := ndb.fastNodeCacheQueue.Remove(oldest).(*FastNode).key - delete(ndb.fastNodeCache, string(key)) + delete(ndb.fastNodeCache, ibytes.UnsafeBytesToStr(key)) } } diff --git a/tree_dotgraph.go b/tree_dotgraph.go index 38df20539..214440cab 100644 --- a/tree_dotgraph.go +++ b/tree_dotgraph.go @@ -5,6 +5,8 @@ import ( "fmt" "io" "text/template" + + ibytes "github.com/cosmos/iavl/internal/bytes" ) type graphEdge struct { @@ -56,12 +58,12 @@ func WriteDOTGraph(w io.Writer, tree *ImmutableTree, paths []PathToLeaf) { } shortHash := graphNode.Hash[:7] - graphNode.Label = mkLabel(string(node.key), 16, "sans-serif") + graphNode.Label = mkLabel(ibytes.UnsafeBytesToStr(node.key), 16, "sans-serif") graphNode.Label += mkLabel(shortHash, 10, "monospace") graphNode.Label += mkLabel(fmt.Sprintf("version=%d", node.version), 10, "monospace") if node.value != nil { - graphNode.Label += mkLabel(string(node.value), 10, "sans-serif") + graphNode.Label += mkLabel(ibytes.UnsafeBytesToStr(node.value), 10, "sans-serif") } if node.height == 0 { diff --git a/unsaved_fast_iterator.go b/unsaved_fast_iterator.go index 69483156f..77c71b57e 100644 --- a/unsaved_fast_iterator.go +++ b/unsaved_fast_iterator.go @@ -6,6 +6,8 @@ import ( "sort" dbm "github.com/tendermint/tm-db" + + ibytes "github.com/cosmos/iavl/internal/bytes" ) var ( @@ -18,29 +20,19 @@ var ( // it iterates over the latest state via fast nodes, // taking advantage of keys being located in sequence in the underlying database. type UnsavedFastIterator struct { - start, end []byte - - valid bool - - ascending bool - - err error - - ndb *nodeDB + start, end []byte + valid bool + ascending bool + err error + ndb *nodeDB + nextKey []byte + nextVal []byte + fastIterator dbm.Iterator + nextUnsavedNodeIdx int unsavedFastNodeAdditions map[string]*FastNode - - unsavedFastNodeRemovals map[string]interface{} - - unsavedFastNodesToSort []string - - nextKey []byte - - nextVal []byte - - nextUnsavedNodeIdx int - - fastIterator dbm.Iterator + unsavedFastNodeRemovals map[string]interface{} + unsavedFastNodesToSort []string } var _ dbm.Iterator = (*UnsavedFastIterator)(nil) @@ -71,7 +63,7 @@ func NewUnsavedFastIterator(start, end []byte, ascending bool, ndb *nodeDB, unsa continue } - iter.unsavedFastNodesToSort = append(iter.unsavedFastNodesToSort, string(fastNode.key)) + iter.unsavedFastNodesToSort = append(iter.unsavedFastNodesToSort, ibytes.UnsafeBytesToStr(fastNode.key)) } sort.Slice(iter.unsavedFastNodesToSort, func(i, j int) bool { @@ -142,8 +134,8 @@ func (iter *UnsavedFastIterator) Next() { return } + diskKeyStr := ibytes.UnsafeBytesToStr(iter.fastIterator.Key()) if iter.fastIterator.Valid() && iter.nextUnsavedNodeIdx < len(iter.unsavedFastNodesToSort) { - diskKeyStr := string(iter.fastIterator.Key()) if iter.unsavedFastNodeRemovals[diskKeyStr] != nil { // If next fast node from disk is to be removed, skip it. @@ -186,7 +178,7 @@ func (iter *UnsavedFastIterator) Next() { // if only nodes on disk are left, we return them if iter.fastIterator.Valid() { - if iter.unsavedFastNodeRemovals[string(iter.fastIterator.Key())] != nil { + if iter.unsavedFastNodeRemovals[diskKeyStr] != nil { // If next fast node from disk is to be removed, skip it. iter.fastIterator.Next() iter.Next() From e334dd3f40554f32bebadb43b51bd4762a177bf3 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Wed, 20 Jul 2022 12:50:32 +0200 Subject: [PATCH 2/6] code style --- mutable_tree.go | 13 ++++++------- nodedb.go | 19 +++++++++---------- tree_dotgraph.go | 6 ++---- unsafe.go | 8 ++++++++ unsaved_fast_iterator.go | 6 ++---- 5 files changed, 27 insertions(+), 25 deletions(-) create mode 100644 unsafe.go diff --git a/mutable_tree.go b/mutable_tree.go index c30984ef9..973834efa 100644 --- a/mutable_tree.go +++ b/mutable_tree.go @@ -9,11 +9,10 @@ import ( "sync" "time" - "github.com/cosmos/iavl/internal/logger" "github.com/pkg/errors" dbm "github.com/tendermint/tm-db" - ibytes "github.com/cosmos/iavl/internal/bytes" + "github.com/cosmos/iavl/internal/logger" ) // ErrVersionDoesNotExist is returned if a requested version does not exist. @@ -147,7 +146,7 @@ func (tree *MutableTree) Get(key []byte) ([]byte, error) { return nil, nil } - if fastNode, ok := tree.unsavedFastNodeAdditions[ibytes.UnsafeBytesToStr(key)]; ok { + if fastNode, ok := tree.unsavedFastNodeAdditions[unsafeToStr(key)]; ok { return fastNode.value, nil } @@ -916,7 +915,7 @@ func (tree *MutableTree) getUnsavedFastNodeRemovals() map[string]interface{} { } func (tree *MutableTree) addUnsavedAddition(key []byte, node *FastNode) { - skey := ibytes.UnsafeBytesToStr(key) + skey := unsafeToStr(key) delete(tree.unsavedFastNodeRemovals, skey) tree.unsavedFastNodeAdditions[skey] = node } @@ -937,7 +936,7 @@ func (tree *MutableTree) saveFastNodeAdditions() error { } func (tree *MutableTree) addUnsavedRemoval(key []byte) { - skey := ibytes.UnsafeBytesToStr(key) + skey := unsafeToStr(key) delete(tree.unsavedFastNodeAdditions, skey) tree.unsavedFastNodeRemovals[skey] = true } @@ -950,7 +949,7 @@ func (tree *MutableTree) saveFastNodeRemovals() error { sort.Strings(keysToSort) for _, key := range keysToSort { - if err := tree.ndb.DeleteFastNode(ibytes.UnsafeStrToBytes(key)); err != nil { + if err := tree.ndb.DeleteFastNode(unsafeToBz(key)); err != nil { return err } } @@ -1230,7 +1229,7 @@ func (tree *MutableTree) addOrphans(orphans []*Node) error { if len(node.hash) == 0 { return fmt.Errorf("expected to find node hash, but was empty") } - tree.orphans[ibytes.UnsafeBytesToStr(node.hash)] = node.version + tree.orphans[unsafeToStr(node.hash)] = node.version } return nil } diff --git a/nodedb.go b/nodedb.go index e41439346..d374a5564 100644 --- a/nodedb.go +++ b/nodedb.go @@ -14,7 +14,6 @@ import ( "github.com/pkg/errors" dbm "github.com/tendermint/tm-db" - ibytes "github.com/cosmos/iavl/internal/bytes" "github.com/cosmos/iavl/internal/logger" ) @@ -93,7 +92,7 @@ func newNodeDB(db dbm.DB, cacheSize int, opts *Options) *nodeDB { opts = &o } - storeVersion, err := db.Get(metadataKeyFormat.Key(ibytes.UnsafeStrToBytes(storageVersionKey))) + storeVersion, err := db.Get(metadataKeyFormat.Key(unsafeToBz(storageVersionKey))) if err != nil || storeVersion == nil { storeVersion = []byte(defaultStorageVersionValue) @@ -126,7 +125,7 @@ func (ndb *nodeDB) GetNode(hash []byte) (*Node, error) { } // Check the cache. - if elem, ok := ndb.nodeCache[ibytes.UnsafeBytesToStr(hash)]; ok { + if elem, ok := ndb.nodeCache[unsafeToStr(hash)]; ok { if ndb.opts.Stat != nil { ndb.opts.Stat.IncCacheHitCnt() } @@ -173,7 +172,7 @@ func (ndb *nodeDB) GetFastNode(key []byte) (*FastNode, error) { } // Check the cache. - if elem, ok := ndb.fastNodeCache[ibytes.UnsafeBytesToStr(key)]; ok { + if elem, ok := ndb.fastNodeCache[unsafeToStr(key)]; ok { if ndb.opts.Stat != nil { ndb.opts.Stat.IncFastCacheHitCnt() } @@ -887,7 +886,7 @@ func (ndb *nodeDB) getFastIterator(start, end []byte, ascending bool) (dbm.Itera } func (ndb *nodeDB) uncacheNode(hash []byte) { - key := ibytes.UnsafeBytesToStr(hash) + key := unsafeToStr(hash) if elem, ok := ndb.nodeCache[key]; ok { ndb.nodeCacheQueue.Remove(elem) delete(ndb.nodeCache, key) @@ -898,17 +897,17 @@ func (ndb *nodeDB) uncacheNode(hash []byte) { // reached the cache size limit. func (ndb *nodeDB) cacheNode(node *Node) { elem := ndb.nodeCacheQueue.PushBack(node) - ndb.nodeCache[ibytes.UnsafeBytesToStr(node.hash)] = elem + ndb.nodeCache[unsafeToStr(node.hash)] = elem if ndb.nodeCacheQueue.Len() > ndb.nodeCacheSize { oldest := ndb.nodeCacheQueue.Front() hash := ndb.nodeCacheQueue.Remove(oldest).(*Node).hash - delete(ndb.nodeCache, ibytes.UnsafeBytesToStr(hash)) + delete(ndb.nodeCache, unsafeToStr(hash)) } } func (ndb *nodeDB) uncacheFastNode(key []byte) { - skey := ibytes.UnsafeBytesToStr(key) + skey := unsafeToStr(key) if elem, ok := ndb.fastNodeCache[skey]; ok { ndb.fastNodeCacheQueue.Remove(elem) delete(ndb.fastNodeCache, skey) @@ -919,12 +918,12 @@ func (ndb *nodeDB) uncacheFastNode(key []byte) { // reached the cache size limit. func (ndb *nodeDB) cacheFastNode(node *FastNode) { elem := ndb.fastNodeCacheQueue.PushBack(node) - ndb.fastNodeCache[ibytes.UnsafeBytesToStr(node.key)] = elem + ndb.fastNodeCache[unsafeToStr(node.key)] = elem if ndb.fastNodeCacheQueue.Len() > ndb.fastNodeCacheSize { oldest := ndb.fastNodeCacheQueue.Front() key := ndb.fastNodeCacheQueue.Remove(oldest).(*FastNode).key - delete(ndb.fastNodeCache, ibytes.UnsafeBytesToStr(key)) + delete(ndb.fastNodeCache, unsafeToStr(key)) } } diff --git a/tree_dotgraph.go b/tree_dotgraph.go index 214440cab..9aa1e8630 100644 --- a/tree_dotgraph.go +++ b/tree_dotgraph.go @@ -5,8 +5,6 @@ import ( "fmt" "io" "text/template" - - ibytes "github.com/cosmos/iavl/internal/bytes" ) type graphEdge struct { @@ -58,12 +56,12 @@ func WriteDOTGraph(w io.Writer, tree *ImmutableTree, paths []PathToLeaf) { } shortHash := graphNode.Hash[:7] - graphNode.Label = mkLabel(ibytes.UnsafeBytesToStr(node.key), 16, "sans-serif") + graphNode.Label = mkLabel(unsafeToStr(node.key), 16, "sans-serif") graphNode.Label += mkLabel(shortHash, 10, "monospace") graphNode.Label += mkLabel(fmt.Sprintf("version=%d", node.version), 10, "monospace") if node.value != nil { - graphNode.Label += mkLabel(ibytes.UnsafeBytesToStr(node.value), 10, "sans-serif") + graphNode.Label += mkLabel(unsafeToStr(node.value), 10, "sans-serif") } if node.height == 0 { diff --git a/unsafe.go b/unsafe.go new file mode 100644 index 000000000..2d00540d7 --- /dev/null +++ b/unsafe.go @@ -0,0 +1,8 @@ +package iavl + +import ibytes "github.com/cosmos/iavl/internal/bytes" + +var ( + unsafeToStr = ibytes.UnsafeBytesToStr + unsafeToBz = ibytes.UnsafeStrToBytes +) diff --git a/unsaved_fast_iterator.go b/unsaved_fast_iterator.go index 77c71b57e..cbbff85fe 100644 --- a/unsaved_fast_iterator.go +++ b/unsaved_fast_iterator.go @@ -6,8 +6,6 @@ import ( "sort" dbm "github.com/tendermint/tm-db" - - ibytes "github.com/cosmos/iavl/internal/bytes" ) var ( @@ -63,7 +61,7 @@ func NewUnsavedFastIterator(start, end []byte, ascending bool, ndb *nodeDB, unsa continue } - iter.unsavedFastNodesToSort = append(iter.unsavedFastNodesToSort, ibytes.UnsafeBytesToStr(fastNode.key)) + iter.unsavedFastNodesToSort = append(iter.unsavedFastNodesToSort, unsafeToStr(fastNode.key)) } sort.Slice(iter.unsavedFastNodesToSort, func(i, j int) bool { @@ -134,7 +132,7 @@ func (iter *UnsavedFastIterator) Next() { return } - diskKeyStr := ibytes.UnsafeBytesToStr(iter.fastIterator.Key()) + diskKeyStr := unsafeToStr(iter.fastIterator.Key()) if iter.fastIterator.Valid() && iter.nextUnsavedNodeIdx < len(iter.unsavedFastNodesToSort) { if iter.unsavedFastNodeRemovals[diskKeyStr] != nil { From 3a5d7f0742221bfde7f8726f3417f777d746b3bb Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Wed, 20 Jul 2022 13:06:33 +0200 Subject: [PATCH 3/6] update changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21338251f..ef3ae2db0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,16 @@ ## Unreleased +### Improvements + +- [#525](https://github.com/cosmos/iavl/pull/525) Optimization: use fast unsafe bytes->string convertion. + ## 0.19.0 (July 6, 2022) ### Breaking Changes - [\514](https://github.com/cosmos/iavl/pull/514) Downgrade Tendermint to 0.34.x -- [\500](https://github.com/cosmos/iavl/pull/500) Return errors instead of panicking. +- [\500](https://github.com/cosmos/iavl/pull/500) Return errors instead of panicking. ### Improvements From 5c367030a19c28d916d43dff4b67ae49a9b5b62b Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Mon, 25 Jul 2022 16:38:13 +0200 Subject: [PATCH 4/6] add fast convertions to cache --- cache/cache.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index c0f36c63d..aed19bdab 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -2,6 +2,8 @@ package cache import ( "container/list" + + ibytes "github.com/cosmos/iavl/internal/bytes" ) // Node represents a node eligible for caching. @@ -59,7 +61,7 @@ func New(maxElementCount int) Cache { } func (c *lruCache) Add(node Node) Node { - if e, exists := c.dict[string(node.GetKey())]; exists { + if e, exists := c.dict[ibytes.UnsafeBytesToStr(node.GetKey())]; exists { c.ll.MoveToFront(e) old := e.Value e.Value = node @@ -67,7 +69,7 @@ func (c *lruCache) Add(node Node) Node { } elem := c.ll.PushFront(node) - c.dict[string(node.GetKey())] = elem + c.dict[ibytes.UnsafeBytesToStr(node.GetKey())] = elem if c.ll.Len() > c.maxElementCount { oldest := c.ll.Back() @@ -78,7 +80,7 @@ func (c *lruCache) Add(node Node) Node { } func (nc *lruCache) Get(key []byte) Node { - if ele, hit := nc.dict[string(key)]; hit { + if ele, hit := nc.dict[ibytes.UnsafeBytesToStr(key)]; hit { nc.ll.MoveToFront(ele) return ele.Value.(Node) } @@ -86,7 +88,7 @@ func (nc *lruCache) Get(key []byte) Node { } func (c *lruCache) Has(key []byte) bool { - _, exists := c.dict[string(key)] + _, exists := c.dict[ibytes.UnsafeBytesToStr(key)] return exists } @@ -95,7 +97,7 @@ func (nc *lruCache) Len() int { } func (c *lruCache) Remove(key []byte) Node { - if elem, exists := c.dict[string(key)]; exists { + if elem, exists := c.dict[ibytes.UnsafeBytesToStr(key)]; exists { return c.remove(elem) } return nil @@ -103,6 +105,6 @@ func (c *lruCache) Remove(key []byte) Node { func (c *lruCache) remove(e *list.Element) Node { removed := c.ll.Remove(e).(Node) - delete(c.dict, string(removed.GetKey())) + delete(c.dict, ibytes.UnsafeBytesToStr(removed.GetKey())) return removed } From 75e727351ee7c5db26aa218fa38afff437fa7d0c Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Wed, 3 Aug 2022 01:31:16 +0200 Subject: [PATCH 5/6] Update CHANGELOG.md Co-authored-by: Roman --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df6066ebb..8c77ef2c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Improvements -- [#525](https://github.com/cosmos/iavl/pull/525) Optimization: use fast unsafe bytes->string convertion. +- [#525](https://github.com/cosmos/iavl/pull/525) Optimization: use fast unsafe bytes->string conversion. ### Bug Fixes From 32036693fb6bac2c6922b524b19cd0caf20383dc Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Wed, 3 Aug 2022 11:03:35 +0200 Subject: [PATCH 6/6] one more tiny value cache --- cache/cache.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index aed19bdab..a4ca88d67 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -61,7 +61,8 @@ func New(maxElementCount int) Cache { } func (c *lruCache) Add(node Node) Node { - if e, exists := c.dict[ibytes.UnsafeBytesToStr(node.GetKey())]; exists { + keyStr := ibytes.UnsafeBytesToStr(node.GetKey()) + if e, exists := c.dict[keyStr]; exists { c.ll.MoveToFront(e) old := e.Value e.Value = node @@ -69,11 +70,10 @@ func (c *lruCache) Add(node Node) Node { } elem := c.ll.PushFront(node) - c.dict[ibytes.UnsafeBytesToStr(node.GetKey())] = elem + c.dict[keyStr] = elem if c.ll.Len() > c.maxElementCount { oldest := c.ll.Back() - return c.remove(oldest) } return nil