diff --git a/actpool/queueworker.go b/actpool/queueworker.go index e1db183a0a..3ccf3b3730 100644 --- a/actpool/queueworker.go +++ b/actpool/queueworker.go @@ -238,8 +238,14 @@ func (worker *queueWorker) Reset(ctx context.Context) { worker.emptyAccounts.Set(from, struct{}{}) return } + var pendingNonce uint64 + if protocol.MustGetFeatureCtx(ctx).UseZeroNonceForFreshAccount { + pendingNonce = confirmedState.PendingNonceConsideringFreshAccount() + } else { + pendingNonce = confirmedState.PendingNonce() + } // Remove all actions that are committed to new block - acts := queue.UpdateAccountState(confirmedState.PendingNonce(), confirmedState.Balance) + acts := queue.UpdateAccountState(pendingNonce, confirmedState.Balance) acts2 := queue.UpdateQueue() worker.ap.removeInvalidActs(append(acts, acts2...)) // Delete the queue entry if it becomes empty diff --git a/db/batch/batch_impl.go b/db/batch/batch_impl.go index 296e99b12e..05d45f0f20 100644 --- a/db/batch/batch_impl.go +++ b/db/batch/batch_impl.go @@ -27,7 +27,7 @@ type ( tag int // latest snapshot + 1 batchShots []int // snapshots of batch are merely size of write queue at time of snapshot caches []KVStoreCache // snapshots of cache - keyTags map[kvCacheKey][]int + keyTags map[kvCacheKey]*kvCacheValue tagKeys [][]kvCacheKey } ) @@ -254,19 +254,19 @@ func (cb *cachedBatch) clear() { cb.tag = 0 cb.batchShots = make([]int, 0) cb.caches = []KVStoreCache{NewKVCache()} - cb.keyTags = map[kvCacheKey][]int{} + cb.keyTags = map[kvCacheKey]*kvCacheValue{} cb.tagKeys = [][]kvCacheKey{{}} } func (cb *cachedBatch) touchKey(h kvCacheKey) { tags, ok := cb.keyTags[h] if !ok { - cb.keyTags[h] = []int{cb.tag} + cb.keyTags[h] = newkvCacheValue([]int{cb.tag}) cb.tagKeys[cb.tag] = append(cb.tagKeys[cb.tag], h) return } - if tags[len(tags)-1] != cb.tag { - cb.keyTags[h] = append(tags, cb.tag) + if tags.last() != cb.tag { + tags.append(cb.tag) cb.tagKeys[cb.tag] = append(cb.tagKeys[cb.tag], h) } } @@ -306,8 +306,8 @@ func (cb *cachedBatch) Get(namespace string, key []byte) ([]byte, error) { var v []byte err := ErrNotExist if tags, ok := cb.keyTags[h]; ok { - for i := len(tags) - 1; i >= 0; i-- { - v, err = cb.caches[tags[i]].Read(&h) + for i := tags.len() - 1; i >= 0; i-- { + v, err = cb.caches[tags.getAt(i)].Read(&h) if errors.Cause(err) == ErrNotExist { continue } @@ -344,8 +344,9 @@ func (cb *cachedBatch) RevertSnapshot(snapshot int) error { for tag := cb.tag; tag < len(cb.tagKeys); tag++ { keys := cb.tagKeys[tag] for _, key := range keys { - cb.keyTags[key] = cb.keyTags[key][:len(cb.keyTags[key])-1] - if len(cb.keyTags[key]) == 0 { + kv := cb.keyTags[key] + kv.pop() + if kv.len() == 0 { delete(cb.keyTags, key) } } @@ -369,12 +370,10 @@ func (cb *cachedBatch) ResetSnapshots() { cb.caches = cb.caches[:1] } keys := make([]kvCacheKey, 0, len(cb.keyTags)) - for key := range cb.keyTags { + for key, v := range cb.keyTags { + v.reset() keys = append(keys, key) } - for _, key := range keys { - cb.keyTags[key] = []int{0} - } cb.tagKeys = [][]kvCacheKey{keys} } diff --git a/db/batch/kv_cache.go b/db/batch/kv_cache.go index 11fc1c132c..f35fdfa502 100644 --- a/db/batch/kv_cache.go +++ b/db/batch/kv_cache.go @@ -27,6 +27,7 @@ type ( key1 string key2 string } + kvCacheValue []int node struct { value []byte @@ -39,6 +40,38 @@ type ( } ) +func newkvCacheValue(v []int) *kvCacheValue { + return (*kvCacheValue)(&v) +} + +func (kv *kvCacheValue) reset() { + ([]int)(*kv)[0] = 0 + *kv = (*kv)[:1] +} + +func (kv *kvCacheValue) pop() { + *kv = (*kv)[:kv.len()-1] +} +func (kv *kvCacheValue) get() []int { + return ([]int)(*kv) +} + +func (kv *kvCacheValue) getAt(i int) int { + return ([]int)(*kv)[i] +} + +func (kv *kvCacheValue) append(v int) { + *kv = append(*kv, v) +} + +func (kv *kvCacheValue) len() int { + return len(*kv) +} + +func (kv *kvCacheValue) last() int { + return (*kv)[len(*kv)-1] +} + // NewKVCache returns a KVCache func NewKVCache() KVStoreCache { return &kvCache{ diff --git a/db/batch/kv_cache_test.go b/db/batch/kv_cache_test.go index bb3d190a79..8bbc1a225c 100644 --- a/db/batch/kv_cache_test.go +++ b/db/batch/kv_cache_test.go @@ -95,6 +95,26 @@ func TestKvCache(t *testing.T) { require.Equal(v, v3) } +func TestKvCacheValue(t *testing.T) { + require := require.New(t) + + c := newkvCacheValue([]int{1}) + require.Equal([]int{1}, c.get()) + require.Equal(1, c.len()) + + c.reset() + require.Equal([]int{0}, c.get()) + + c.append(3) + require.Equal([]int{0, 3}, c.get()) + require.Equal(0, c.getAt(0)) + require.Equal(3, c.last()) + require.Equal(2, c.len()) + c.pop() + require.Equal([]int{0}, c.get()) + require.Equal(1, c.len()) +} + func TestWriteIfNotExist(t *testing.T) { require := require.New(t) @@ -114,3 +134,37 @@ func TestWriteIfNotExist(t *testing.T) { err = c.WriteIfNotExist(k1, v1) require.NoError(err) } + +func BenchmarkMapKey_ValueOperate(b *testing.B) { + keyTags := map[kvCacheKey]*kvCacheValue{} + keyTags2 := map[kvCacheKey][]int{} + for i := 0; i < 10000; i++ { + key := kvCacheKey{ + key1: string(make([]byte, 5)), + key2: string(make([]byte, 32)), + } + keyTags[key] = newkvCacheValue([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) + keyTags2[key] = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + } + b.Run("reset by map key", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for k := range keyTags { + keyTags[k].reset() + } + } + }) + b.Run("reset by map value, 2x faster", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, v := range keyTags { + v.reset() + } + } + }) + b.Run("orign map assign", func(b *testing.B) { + for i := 0; i < b.N; i++ { + for k := range keyTags2 { + keyTags2[k] = []int{0} + } + } + }) +}