Skip to content

Commit 656f3ad

Browse files
authored
Merge pull request #2020 from onetechnical/onetechnical/relbeta2.5.3
go-algorand 2.5.3-beta
2 parents 43373ba + 2a37f65 commit 656f3ad

15 files changed

+187
-36
lines changed

buildnumber.dat

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2
1+
3

data/pools/transactionPool.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Lo
109109
}
110110
pool.cond.L = &pool.mu
111111
pool.assemblyCond.L = &pool.assemblyMu
112-
pool.recomputeBlockEvaluator(make(map[transactions.Txid]basics.Round))
112+
pool.recomputeBlockEvaluator(make(map[transactions.Txid]basics.Round), 0)
113113
return &pool
114114
}
115115

@@ -162,7 +162,7 @@ func (pool *TransactionPool) Reset() {
162162
pool.numPendingWholeBlocks = 0
163163
pool.pendingBlockEvaluator = nil
164164
pool.statusCache.reset()
165-
pool.recomputeBlockEvaluator(make(map[transactions.Txid]basics.Round))
165+
pool.recomputeBlockEvaluator(make(map[transactions.Txid]basics.Round), 0)
166166
}
167167

168168
// NumExpired returns the number of transactions that expired at the
@@ -468,10 +468,10 @@ func (pool *TransactionPool) OnNewBlock(block bookkeeping.Block, delta ledgercor
468468
var knownCommitted uint
469469
var unknownCommitted uint
470470

471-
commitedTxids := delta.Txids
471+
committedTxids := delta.Txids
472472
if pool.logProcessBlockStats {
473473
pool.pendingMu.RLock()
474-
for txid := range commitedTxids {
474+
for txid := range committedTxids {
475475
if _, ok := pool.pendingTxids[txid]; ok {
476476
knownCommitted++
477477
} else {
@@ -512,7 +512,7 @@ func (pool *TransactionPool) OnNewBlock(block bookkeeping.Block, delta ledgercor
512512
// Recompute the pool by starting from the new latest block.
513513
// This has the side-effect of discarding transactions that
514514
// have been committed (or that are otherwise no longer valid).
515-
stats = pool.recomputeBlockEvaluator(commitedTxids)
515+
stats = pool.recomputeBlockEvaluator(committedTxids, knownCommitted)
516516
}
517517

518518
stats.KnownCommittedCount = knownCommitted
@@ -625,7 +625,7 @@ func (pool *TransactionPool) addToPendingBlockEvaluator(txgroup []transactions.S
625625
// recomputeBlockEvaluator constructs a new BlockEvaluator and feeds all
626626
// in-pool transactions to it (removing any transactions that are rejected
627627
// by the BlockEvaluator). Expects that the pool.mu mutex would be already taken.
628-
func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transactions.Txid]basics.Round) (stats telemetryspec.ProcessBlockMetrics) {
628+
func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transactions.Txid]basics.Round, knownCommitted uint) (stats telemetryspec.ProcessBlockMetrics) {
629629
pool.pendingBlockEvaluator = nil
630630

631631
latest := pool.ledger.Latest()
@@ -665,7 +665,11 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transact
665665

666666
next := bookkeeping.MakeBlock(prev)
667667
pool.numPendingWholeBlocks = 0
668-
pool.pendingBlockEvaluator, err = pool.ledger.StartEvaluator(next.BlockHeader, pendingCount)
668+
hint := pendingCount - int(knownCommitted)
669+
if hint < 0 || int(knownCommitted) < 0 {
670+
hint = 0
671+
}
672+
pool.pendingBlockEvaluator, err = pool.ledger.StartEvaluator(next.BlockHeader, hint)
669673
if err != nil {
670674
pool.log.Warnf("TransactionPool.recomputeBlockEvaluator: cannot start evaluator: %v", err)
671675
return

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/algorand/graphtrace v0.0.0-20201117160756-e524ed1a6f64
99
github.com/algorand/msgp v1.1.47
1010
github.com/algorand/oapi-codegen v1.3.5-algorand5
11-
github.com/algorand/websocket v1.4.1
11+
github.com/algorand/websocket v1.4.2
1212
github.com/aws/aws-sdk-go v1.16.5
1313
github.com/cpuguy83/go-md2man v1.0.8 // indirect
1414
github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ github.com/algorand/msgp v1.1.47 h1:xeU6G/Mb1iudJe4L5X38YrOY+VHhvHQDZXxyXYHTzOw=
3535
github.com/algorand/msgp v1.1.47/go.mod h1:LtOntbYiCHj/Sl/Sqxtf8CZOrDt2a8Dv3tLaS6mcnUE=
3636
github.com/algorand/oapi-codegen v1.3.5-algorand5 h1:y576Ca2/guQddQrQA7dtL5KcOx5xQgPeIupiuFMGyCI=
3737
github.com/algorand/oapi-codegen v1.3.5-algorand5/go.mod h1:/k0Ywn0lnt92uBMyE+yiRf/Wo3/chxHHsAfenD09EbY=
38-
github.com/algorand/websocket v1.4.1 h1:FPoNHI8i2VZWZzhCscY8JTzsAE7Vv73753cMbzb3udk=
39-
github.com/algorand/websocket v1.4.1/go.mod h1:0nFSn+xppw/GZS9hgWPS3b8/4FcA3Pj7XQxm+wqHGx8=
38+
github.com/algorand/websocket v1.4.2 h1:zMB7ukz+c7tcef8rVqmKQTv6KQtxXtCFuiAqKaE7n9I=
39+
github.com/algorand/websocket v1.4.2/go.mod h1:0nFSn+xppw/GZS9hgWPS3b8/4FcA3Pj7XQxm+wqHGx8=
4040
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
4141
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
4242
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=

ledger/appcow.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ func MakeDebugBalances(l ledgerForCowBase, round basics.Round, proto protocol.Co
410410
// Execution happens in a child cow and all modifications are merged into parent if the program passes
411411
func (cb *roundCowState) StatefulEval(params logic.EvalParams, aidx basics.AppIndex, program []byte) (pass bool, evalDelta basics.EvalDelta, err error) {
412412
// Make a child cow to eval our program in
413-
calf := cb.child()
413+
calf := cb.child(1)
414414
params.Ledger, err = newLogicLedger(calf, aidx)
415415
if err != nil {
416416
return false, basics.EvalDelta{}, err

ledger/appcow_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ func TestCowStorage(t *testing.T) {
305305
// Make a child
306306
if childDepth < maxChildDepth && rand.Float32() < 0.1 {
307307
lastParent = cow
308-
cow = cow.child()
308+
cow = cow.child(1)
309309
childDepth++
310310
}
311311

ledger/apply/asset.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ import (
2424
)
2525

2626
func cloneAssetHoldings(m map[basics.AssetIndex]basics.AssetHolding) map[basics.AssetIndex]basics.AssetHolding {
27-
res := make(map[basics.AssetIndex]basics.AssetHolding)
27+
res := make(map[basics.AssetIndex]basics.AssetHolding, len(m))
2828
for id, val := range m {
2929
res[id] = val
3030
}
3131
return res
3232
}
3333

3434
func cloneAssetParams(m map[basics.AssetIndex]basics.AssetParams) map[basics.AssetIndex]basics.AssetParams {
35-
res := make(map[basics.AssetIndex]basics.AssetParams)
35+
res := make(map[basics.AssetIndex]basics.AssetParams, len(m))
3636
for id, val := range m {
3737
res[id] = val
3838
}

ledger/apply/asset_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package apply
1818

1919
import (
20+
"math/rand"
2021
"testing"
2122

2223
"github.com/stretchr/testify/require"
@@ -97,3 +98,19 @@ func TestAssetTransfer(t *testing.T) {
9798
require.Equal(t, dstAmount-toSend, addrs[cls].Assets[1].Amount)
9899
}
99100
}
101+
102+
var benchTotal int = 0
103+
104+
func BenchmarkAssetCloning(b *testing.B) {
105+
const numAssets = 800
106+
assets := make(map[basics.AssetIndex]basics.AssetHolding, numAssets)
107+
for j := 0; j < numAssets; j++ {
108+
aidx := basics.AssetIndex(rand.Int63n(100000000))
109+
assets[aidx] = basics.AssetHolding{Amount: uint64(aidx)}
110+
}
111+
b.ResetTimer()
112+
for i := 0; i < b.N; i++ {
113+
clone := cloneAssetHoldings(assets)
114+
benchTotal += len(clone) // make sure the compiler does not optimize out cloneAssetHoldings call
115+
}
116+
}

ledger/cow.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,12 @@ func (cb *roundCowState) setCompactCertNext(rnd basics.Round) {
195195
cb.mods.CompactCertNext = rnd
196196
}
197197

198-
func (cb *roundCowState) child() *roundCowState {
198+
func (cb *roundCowState) child(hint int) *roundCowState {
199199
return &roundCowState{
200200
lookupParent: cb,
201201
commitParent: cb,
202202
proto: cb.proto,
203-
mods: ledgercore.MakeStateDelta(cb.mods.Hdr, cb.mods.PrevTimestamp, 1),
203+
mods: ledgercore.MakeStateDelta(cb.mods.Hdr, cb.mods.PrevTimestamp, hint),
204204
sdeltas: make(map[basics.Address]map[storagePtr]*storageDelta),
205205
}
206206
}

ledger/cow_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func TestCowBalance(t *testing.T) {
105105
c0 := makeRoundCowState(&ml, bookkeeping.BlockHeader{}, 0, 0)
106106
checkCow(t, c0, accts0)
107107

108-
c1 := c0.child()
108+
c1 := c0.child(0)
109109
checkCow(t, c0, accts0)
110110
checkCow(t, c1, accts0)
111111

@@ -114,7 +114,7 @@ func TestCowBalance(t *testing.T) {
114114
checkCow(t, c0, accts0)
115115
checkCow(t, c1, accts1)
116116

117-
c2 := c1.child()
117+
c2 := c1.child(0)
118118
checkCow(t, c0, accts0)
119119
checkCow(t, c1, accts1)
120120
checkCow(t, c2, accts1)

ledger/eval.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ func (eval *BlockEvaluator) TestTransactionGroup(txgroup []transactions.SignedTx
581581
return fmt.Errorf("group size %d exceeds maximum %d", len(txgroup), eval.proto.MaxTxGroupSize)
582582
}
583583

584-
cow := eval.state.child()
584+
cow := eval.state.child(len(txgroup))
585585

586586
var group transactions.TxGroup
587587
for gi, txn := range txgroup {
@@ -715,7 +715,7 @@ func (eval *BlockEvaluator) transactionGroup(txgroup []transactions.SignedTxnWit
715715
var group transactions.TxGroup
716716
var groupTxBytes int
717717

718-
cow := eval.state.child()
718+
cow := eval.state.child(len(txgroup))
719719

720720
// Prepare eval params for any ApplicationCall transactions in the group
721721
evalParams := eval.prepareEvalParams(txgroup)
@@ -1009,6 +1009,7 @@ func (eval *BlockEvaluator) endOfBlock() error {
10091009

10101010
// FinalValidation does the validation that must happen after the block is built and all state updates are computed
10111011
func (eval *BlockEvaluator) finalValidation() error {
1012+
eval.state.mods.OptimizeAllocatedMemory(eval.proto)
10121013
if eval.validate {
10131014
// check commitments
10141015
txnRoot, err := eval.block.PaysetCommit()

ledger/ledgercore/statedelta.go

+63-6
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,20 @@
1717
package ledgercore
1818

1919
import (
20+
"github.com/algorand/go-algorand/config"
2021
"github.com/algorand/go-algorand/data/basics"
2122
"github.com/algorand/go-algorand/data/bookkeeping"
2223
"github.com/algorand/go-algorand/data/transactions"
2324
)
2425

26+
const (
27+
accountArrayEntrySize = uint64(232) // Measured by BenchmarkBalanceRecord
28+
accountMapCacheEntrySize = uint64(64) // Measured by BenchmarkAcctCache
29+
txleasesEntrySize = uint64(112) // Measured by BenchmarkTxLeases
30+
creatablesEntrySize = uint64(100) // Measured by BenchmarkCreatables
31+
stateDeltaTargetOptimizationThreshold = uint64(50000000)
32+
)
33+
2534
// ModifiedCreatable defines the changes to a single single creatable state
2635
type ModifiedCreatable struct {
2736
// Type of the creatable: app or asset
@@ -68,6 +77,9 @@ type StateDelta struct {
6877

6978
// previous block timestamp
7079
PrevTimestamp int64
80+
81+
// initial hint for allocating data structures for StateDelta
82+
initialTransactionsCount int
7183
}
7284

7385
// AccountDeltas stores ordered accounts and allows fast lookup by address
@@ -78,18 +90,22 @@ type AccountDeltas struct {
7890
acctsCache map[basics.Address]int
7991
}
8092

81-
// MakeStateDelta creates a new instance of StateDelta
93+
// MakeStateDelta creates a new instance of StateDelta.
94+
// hint is amount of transactions for evaluation, 2 * hint is for sender and receiver balance records.
95+
// This does not play well for AssetConfig and ApplicationCall transactions on scale
8296
func MakeStateDelta(hdr *bookkeeping.BlockHeader, prevTimestamp int64, hint int) StateDelta {
8397
return StateDelta{
8498
Accts: AccountDeltas{
8599
accts: make([]basics.BalanceRecord, 0, hint*2),
86100
acctsCache: make(map[basics.Address]int, hint*2),
87101
},
88-
Txids: make(map[transactions.Txid]basics.Round, hint),
89-
Txleases: make(map[Txlease]basics.Round, hint),
90-
Creatables: make(map[basics.CreatableIndex]ModifiedCreatable, hint),
91-
Hdr: hdr,
92-
PrevTimestamp: prevTimestamp,
102+
Txids: make(map[transactions.Txid]basics.Round, hint),
103+
Txleases: make(map[Txlease]basics.Round, hint),
104+
// asset or application creation are considered as rare events so do not pre-allocate space for them
105+
Creatables: make(map[basics.CreatableIndex]ModifiedCreatable),
106+
Hdr: hdr,
107+
PrevTimestamp: prevTimestamp,
108+
initialTransactionsCount: hint,
93109
}
94110
}
95111

@@ -149,3 +165,44 @@ func (ad *AccountDeltas) upsert(br basics.BalanceRecord) {
149165
}
150166
ad.acctsCache[addr] = last
151167
}
168+
169+
// OptimizeAllocatedMemory by reallocating maps to needed capacity
170+
// For each data structure, reallocate if it would save us at least 50MB aggregate
171+
func (sd *StateDelta) OptimizeAllocatedMemory(proto config.ConsensusParams) {
172+
// accts takes up 232 bytes per entry, and is saved for 320 rounds
173+
if uint64(cap(sd.Accts.accts)-len(sd.Accts.accts))*accountArrayEntrySize*proto.MaxBalLookback > stateDeltaTargetOptimizationThreshold {
174+
accts := make([]basics.BalanceRecord, len(sd.Accts.acctsCache))
175+
copy(accts, sd.Accts.accts)
176+
sd.Accts.accts = accts
177+
}
178+
179+
// acctsCache takes up 64 bytes per entry, and is saved for 320 rounds
180+
// realloc if original allocation capacity greater than length of data, and space difference is significant
181+
if 2*sd.initialTransactionsCount > len(sd.Accts.acctsCache) &&
182+
uint64(2*sd.initialTransactionsCount-len(sd.Accts.acctsCache))*accountMapCacheEntrySize*proto.MaxBalLookback > stateDeltaTargetOptimizationThreshold {
183+
acctsCache := make(map[basics.Address]int, len(sd.Accts.acctsCache))
184+
for k, v := range sd.Accts.acctsCache {
185+
acctsCache[k] = v
186+
}
187+
sd.Accts.acctsCache = acctsCache
188+
}
189+
190+
// TxLeases takes up 112 bytes per entry, and is saved for 1000 rounds
191+
if sd.initialTransactionsCount > len(sd.Txleases) &&
192+
uint64(sd.initialTransactionsCount-len(sd.Txleases))*txleasesEntrySize*proto.MaxTxnLife > stateDeltaTargetOptimizationThreshold {
193+
txLeases := make(map[Txlease]basics.Round, len(sd.Txleases))
194+
for k, v := range sd.Txleases {
195+
txLeases[k] = v
196+
}
197+
sd.Txleases = txLeases
198+
}
199+
200+
// Creatables takes up 100 bytes per entry, and is saved for 320 rounds
201+
if uint64(len(sd.Creatables))*creatablesEntrySize*proto.MaxBalLookback > stateDeltaTargetOptimizationThreshold {
202+
creatableDeltas := make(map[basics.CreatableIndex]ModifiedCreatable, len(sd.Creatables))
203+
for k, v := range sd.Creatables {
204+
creatableDeltas[k] = v
205+
}
206+
sd.Creatables = creatableDeltas
207+
}
208+
}

ledger/ledgercore/statedelta_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,60 @@ func TestAccountDeltas(t *testing.T) {
9292
a.Equal(addr1, address)
9393
a.Equal(sample1, data)
9494
}
95+
96+
func BenchmarkMakeStateDelta(b *testing.B) {
97+
hint := 23000
98+
b.ReportAllocs()
99+
b.ResetTimer()
100+
for i := 0; i < b.N; i++ {
101+
MakeStateDelta(nil, 0, hint)
102+
}
103+
}
104+
105+
func BenchmarkBalanceRecord(b *testing.B) {
106+
hint := 23000
107+
b.ReportAllocs()
108+
b.ResetTimer()
109+
for i := 0; i < b.N; i++ {
110+
x := make([]basics.BalanceRecord, 0, hint*2)
111+
if len(x) > 0 {
112+
return
113+
}
114+
}
115+
}
116+
117+
func BenchmarkAcctCache(b *testing.B) {
118+
hint := 23000
119+
b.ReportAllocs()
120+
b.ResetTimer()
121+
for i := 0; i < b.N; i++ {
122+
x := make(map[basics.Address]int, hint*2)
123+
if len(x) > 0 {
124+
return
125+
}
126+
}
127+
}
128+
129+
func BenchmarkCreatables(b *testing.B) {
130+
hint := 23000
131+
b.ReportAllocs()
132+
b.ResetTimer()
133+
for i := 0; i < b.N; i++ {
134+
x := make(map[basics.CreatableIndex]ModifiedCreatable, hint)
135+
if len(x) > 0 {
136+
return
137+
}
138+
}
139+
}
140+
141+
func BenchmarkTxLeases(b *testing.B) {
142+
hint := 23000
143+
b.ReportAllocs()
144+
b.ResetTimer()
145+
for i := 0; i < b.N; i++ {
146+
x := make(map[Txlease]basics.Round, hint)
147+
if len(x) > 0 {
148+
return
149+
}
150+
}
151+
}

network/limited_reader_slurper.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (s *LimitedReaderSlurper) Read(reader io.Reader) error {
7777
// if we received err == nil and n == 0, we should retry calling the Read function.
7878
continue
7979
default:
80-
// if we recieved a non-io.EOF error, return it.
80+
// if we received a non-io.EOF error, return it.
8181
return err
8282
}
8383
}

0 commit comments

Comments
 (0)