-
Notifications
You must be signed in to change notification settings - Fork 215
Proofs pool improvements #6878
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proofs pool improvements #6878
Changes from 6 commits
35f8e8a
91fd46e
7066189
147c0d3
c21c78c
5704d4e
3a1a9e2
e03e7a1
b2fc09a
ac7f4ab
b759ac8
3cb4bce
57532ba
36ae586
4ea5d8e
21fd3ea
210f52b
acf47c7
ecd4e54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package proofscache | ||
|
|
||
| import "github.com/multiversx/mx-chain-core-go/data" | ||
|
|
||
| // NewProofsCache - | ||
| func NewProofsCache(bucketSize int) *proofsCache { | ||
| return newProofsCache(bucketSize) | ||
| } | ||
|
|
||
| // HeadBucketSize - | ||
| func (pc *proofsCache) HeadBucketSize() int { | ||
| if len(pc.proofsByNonceBuckets) > 0 { | ||
| return len(pc.proofsByNonceBuckets[0].proofsByNonce) | ||
| } | ||
|
|
||
| return 0 | ||
| } | ||
|
|
||
| // HeadBucketSize - | ||
| func (pc *proofsCache) FullProofsByNonceSize() int { | ||
| size := 0 | ||
|
|
||
| for _, bucket := range pc.proofsByNonceBuckets { | ||
| size += bucket.size() | ||
| } | ||
|
|
||
| return size | ||
| } | ||
|
|
||
| // ProofsByHashSize - | ||
| func (pc *proofsCache) ProofsByHashSize() int { | ||
| return len(pc.proofsByHash) | ||
| } | ||
|
|
||
| // AddProof - | ||
| func (pc *proofsCache) AddProof(proof data.HeaderProofHandler) { | ||
| pc.addProof(proof) | ||
| } | ||
|
|
||
| // CleanupProofsBehindNonce - | ||
| func (pc *proofsCache) CleanupProofsBehindNonce(nonce uint64) { | ||
| pc.cleanupProofsBehindNonce(nonce) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package proofscache | ||
|
|
||
| import "github.com/multiversx/mx-chain-core-go/data" | ||
|
|
||
| type proofNonceBucket struct { | ||
| maxNonce uint64 | ||
| proofsByNonce []*proofNonceMapping | ||
| bucketSize int | ||
| } | ||
|
|
||
| func newProofBucket(bucketSize int) *proofNonceBucket { | ||
| return &proofNonceBucket{ | ||
| proofsByNonce: make([]*proofNonceMapping, 0), | ||
| bucketSize: bucketSize, | ||
| } | ||
| } | ||
|
|
||
| func (p *proofNonceBucket) size() int { | ||
| return len(p.proofsByNonce) | ||
| } | ||
|
|
||
| func (p *proofNonceBucket) isFull() bool { | ||
|
||
| return len(p.proofsByNonce) >= p.bucketSize | ||
| } | ||
|
|
||
| func (p *proofNonceBucket) insertInNew(proof data.HeaderProofHandler) { | ||
| p.insert(proof) | ||
| p.maxNonce = proof.GetHeaderNonce() | ||
| } | ||
|
|
||
| func (p *proofNonceBucket) insertInExisting(proof data.HeaderProofHandler) { | ||
| p.insert(proof) | ||
|
|
||
| if proof.GetHeaderNonce() > p.maxNonce { | ||
| p.maxNonce = proof.GetHeaderNonce() | ||
| } | ||
| } | ||
|
||
|
|
||
| func (p *proofNonceBucket) insert(proof data.HeaderProofHandler) { | ||
| p.proofsByNonce = append(p.proofsByNonce, &proofNonceMapping{ | ||
| headerHash: string(proof.GetHeaderHash()), | ||
| nonce: proof.GetHeaderNonce(), | ||
| }) | ||
| } | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| package proofscache | ||
|
|
||
| import ( | ||
| "sort" | ||
| "sync" | ||
|
|
||
| "github.com/multiversx/mx-chain-core-go/data" | ||
|
|
@@ -13,22 +12,26 @@ type proofNonceMapping struct { | |
| } | ||
|
|
||
| type proofsCache struct { | ||
| mutProofsCache sync.RWMutex | ||
| proofsByNonce []*proofNonceMapping | ||
| proofsByHash map[string]data.HeaderProofHandler | ||
| mutProofsByNonce sync.RWMutex | ||
| proofsByNonceBuckets []*proofNonceBucket | ||
|
||
| bucketSize int | ||
|
|
||
| proofsByHash map[string]data.HeaderProofHandler | ||
| mutProofsByHash sync.RWMutex | ||
| } | ||
|
|
||
| func newProofsCache() *proofsCache { | ||
| func newProofsCache(bucketSize int) *proofsCache { | ||
|
|
||
| return &proofsCache{ | ||
| mutProofsCache: sync.RWMutex{}, | ||
| proofsByNonce: make([]*proofNonceMapping, 0), | ||
| proofsByHash: make(map[string]data.HeaderProofHandler), | ||
| proofsByNonceBuckets: make([]*proofNonceBucket, 0), | ||
| bucketSize: bucketSize, | ||
| proofsByHash: make(map[string]data.HeaderProofHandler), | ||
| } | ||
| } | ||
|
|
||
| func (pc *proofsCache) getProofByHash(headerHash []byte) (data.HeaderProofHandler, error) { | ||
| pc.mutProofsCache.RLock() | ||
| defer pc.mutProofsCache.RUnlock() | ||
| pc.mutProofsByHash.RLock() | ||
| defer pc.mutProofsByHash.RUnlock() | ||
|
|
||
| proof, ok := pc.proofsByHash[string(headerHash)] | ||
| if !ok { | ||
|
|
@@ -43,39 +46,76 @@ func (pc *proofsCache) addProof(proof data.HeaderProofHandler) { | |
| return | ||
| } | ||
|
|
||
| pc.mutProofsCache.Lock() | ||
| defer pc.mutProofsCache.Unlock() | ||
| pc.insertProofByNonce(proof) | ||
|
|
||
| pc.proofsByNonce = append(pc.proofsByNonce, &proofNonceMapping{ | ||
| headerHash: string(proof.GetHeaderHash()), | ||
| nonce: proof.GetHeaderNonce(), | ||
| }) | ||
| pc.mutProofsByHash.Lock() | ||
| pc.proofsByHash[string(proof.GetHeaderHash())] = proof | ||
| pc.mutProofsByHash.Unlock() | ||
|
||
| } | ||
|
|
||
| sort.Slice(pc.proofsByNonce, func(i, j int) bool { | ||
| return pc.proofsByNonce[i].nonce < pc.proofsByNonce[j].nonce | ||
| }) | ||
| func (pc *proofsCache) insertProofByNonce(proof data.HeaderProofHandler) { | ||
| pc.mutProofsByNonce.Lock() | ||
| defer pc.mutProofsByNonce.Unlock() | ||
|
|
||
| pc.proofsByHash[string(proof.GetHeaderHash())] = proof | ||
| if len(pc.proofsByNonceBuckets) == 0 { | ||
| pc.insertInNewBucket(proof) | ||
| return | ||
| } | ||
|
|
||
| headBucket := pc.proofsByNonceBuckets[0] | ||
|
|
||
| if headBucket.isFull() { | ||
|
||
| pc.insertInNewBucket(proof) | ||
| return | ||
| } | ||
|
|
||
| headBucket.insertInExisting(proof) | ||
| } | ||
|
|
||
| func (pc *proofsCache) insertInNewBucket(proof data.HeaderProofHandler) { | ||
| bucket := newProofBucket(pc.bucketSize) | ||
| bucket.insertInNew(proof) | ||
|
|
||
| pc.proofsByNonceBuckets = append([]*proofNonceBucket{bucket}, pc.proofsByNonceBuckets...) | ||
| } | ||
|
|
||
| func (pc *proofsCache) cleanupProofsBehindNonce(nonce uint64) { | ||
| if nonce == 0 { | ||
| return | ||
| } | ||
|
|
||
| pc.mutProofsCache.Lock() | ||
| defer pc.mutProofsCache.Unlock() | ||
| pc.mutProofsByNonce.Lock() | ||
|
||
| defer pc.mutProofsByNonce.Unlock() | ||
|
|
||
| buckets := make([]*proofNonceBucket, 0) | ||
|
|
||
| wg := &sync.WaitGroup{} | ||
|
|
||
| for _, bucket := range pc.proofsByNonceBuckets { | ||
| if nonce > bucket.maxNonce { | ||
| wg.Add(1) | ||
|
|
||
| proofsByNonce := make([]*proofNonceMapping, 0) | ||
| go func(bucket *proofNonceBucket) { | ||
| pc.cleanupProofsInBucket(bucket) | ||
| wg.Done() | ||
| }(bucket) | ||
|
||
|
|
||
| for _, proofInfo := range pc.proofsByNonce { | ||
| if proofInfo.nonce < nonce { | ||
| delete(pc.proofsByHash, proofInfo.headerHash) | ||
| continue | ||
| } | ||
|
|
||
| proofsByNonce = append(proofsByNonce, proofInfo) | ||
| buckets = append(buckets, bucket) | ||
| } | ||
|
|
||
| pc.proofsByNonce = proofsByNonce | ||
| wg.Wait() | ||
|
|
||
| pc.proofsByNonceBuckets = buckets | ||
| } | ||
|
|
||
| func (pc *proofsCache) cleanupProofsInBucket(bucket *proofNonceBucket) { | ||
| pc.mutProofsByHash.Lock() | ||
| defer pc.mutProofsByHash.Unlock() | ||
|
||
|
|
||
| for _, proofInfo := range bucket.proofsByNonce { | ||
| delete(pc.proofsByHash, proofInfo.headerHash) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package proofscache_test | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we move the test to package There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would keep it in _test package since there are other useful functions from test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and we plan to make this component more generic and to export it in other packages as well (for headers pool) |
||
|
|
||
| import ( | ||
| "fmt" | ||
| "testing" | ||
|
|
||
| proofscache "github.com/multiversx/mx-chain-go/dataRetriever/dataPool/proofsCache" | ||
| ) | ||
|
|
||
| func Benchmark_AddProof_Bucket10_Pool1000(b *testing.B) { | ||
| benchmarkAddProof(b, 10, 1000) | ||
| } | ||
|
|
||
| func Benchmark_AddProof_Bucket100_Pool10000(b *testing.B) { | ||
| benchmarkAddProof(b, 100, 10000) | ||
| } | ||
|
|
||
| func Benchmark_AddProof_Bucket1000_Pool100000(b *testing.B) { | ||
| benchmarkAddProof(b, 1000, 100000) | ||
| } | ||
|
Comment on lines
+10
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe also add some output / statistics in the PR description. |
||
|
|
||
| func benchmarkAddProof(b *testing.B, bucketSize int, nonceRange int) { | ||
| pc := proofscache.NewProofsCache(bucketSize) | ||
|
|
||
| b.ResetTimer() | ||
| for i := 0; i < b.N; i++ { | ||
| b.StopTimer() | ||
| proof := generateProof() | ||
| nonce := generateRandomNonce(int64(nonceRange)) | ||
|
|
||
| proof.HeaderNonce = nonce | ||
| proof.HeaderHash = []byte("hash_" + fmt.Sprintf("%d", nonce)) | ||
| b.StartTimer() | ||
|
|
||
| pc.AddProof(proof) | ||
| } | ||
| } | ||
|
|
||
| func Benchmark_CleanupProofs_Bucket10_Pool1000(b *testing.B) { | ||
| benchmarkCleanupProofs(b, 10, 1000) | ||
| } | ||
|
|
||
| func Benchmark_CleanupProofs_Bucket100_Pool10000(b *testing.B) { | ||
| benchmarkCleanupProofs(b, 100, 10000) | ||
| } | ||
|
|
||
| func Benchmark_CleanupProofs_Bucket1000_Pool100000(b *testing.B) { | ||
| benchmarkCleanupProofs(b, 1000, 100000) | ||
| } | ||
|
|
||
| func benchmarkCleanupProofs(b *testing.B, bucketSize int, nonceRange int) { | ||
| pc := proofscache.NewProofsCache(bucketSize) | ||
|
|
||
| for i := uint64(0); i < uint64(nonceRange); i++ { | ||
| proof := generateProof() | ||
| proof.HeaderNonce = i | ||
| proof.HeaderHash = []byte("hash_" + fmt.Sprintf("%d", i)) | ||
|
|
||
| pc.AddProof(proof) | ||
| } | ||
|
|
||
| b.ResetTimer() | ||
| for i := 0; i < b.N; i++ { | ||
| pc.CleanupProofsBehindNonce(uint64(nonceRange)) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe use a map then the upsert would not need to iterate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to use map