store/cache: speed up + conserve memory by using map clearing idiom in Reset (#6700)

Noticed during my audit, the code for cache.CommitKVStoreCacheManager.Reset()
discarded the prior on every invocation i.e.

    m = make(map[T(key)]T(value))

However, this can be made fast and conserve memory by using the map clearing idiom
that the Go compiler recognizes i.e.

    for key := range m {
        delete(m, key)
    }

and turns into very fast code, instead of the extraneous map discarding.

The speed up generated is:

```shell
$ benchstat before after
name     old time/op    new time/op    delta
Reset-8     204ns ± 2%      66ns ± 9%   -67.86%  (p=0.000 n=20+20)

name     old alloc/op   new alloc/op   delta
Reset-8      384B ± 0%        0B       -100.00%  (p=0.000 n=20+20)

name     old allocs/op  new allocs/op  delta
Reset-8      3.00 ± 0%      0.00       -100.00%  (p=0.000 n=20+20)
```

Fixes #6681
This commit is contained in:
Emmanuel T Odeke 2020-07-13 03:49:09 -07:00 committed by GitHub
parent 5d50e13425
commit d36c6be529
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 1 deletions

48
store/cache/benchmark_test.go vendored Normal file
View File

@ -0,0 +1,48 @@
package cache
import (
"testing"
"github.com/cosmos/cosmos-sdk/store/types"
)
func freshMgr() *CommitKVStoreCacheManager {
return &CommitKVStoreCacheManager{
caches: map[string]types.CommitKVStore{
"a1": nil,
"alalalalalal": nil,
},
}
}
func populate(mgr *CommitKVStoreCacheManager) {
mgr.caches["this one"] = (types.CommitKVStore)(nil)
mgr.caches["those ones are the ones"] = (types.CommitKVStore)(nil)
mgr.caches["very huge key right here and there are we going to ones are the ones"] = (types.CommitKVStore)(nil)
}
func BenchmarkReset(b *testing.B) {
mgr := freshMgr()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
mgr.Reset()
if len(mgr.caches) != 0 {
b.Fatal("Reset failed")
}
populate(mgr)
if len(mgr.caches) == 0 {
b.Fatal("populate failed")
}
mgr.Reset()
if len(mgr.caches) != 0 {
b.Fatal("Reset failed")
}
}
if mgr == nil {
b.Fatal("Impossible condition")
}
}

View File

@ -81,7 +81,12 @@ func (cmgr *CommitKVStoreCacheManager) Unwrap(key types.StoreKey) types.CommitKV
// Reset resets in the internal caches.
func (cmgr *CommitKVStoreCacheManager) Reset() {
cmgr.caches = make(map[string]types.CommitKVStore)
// Clear the map.
// Please note that we are purposefully using the map clearing idiom.
// See https://github.com/cosmos/cosmos-sdk/issues/6681.
for key := range cmgr.caches {
delete(cmgr.caches, key)
}
}
// CacheWrap returns the inter-block cache as a cache-wrapped CommitKVStore.