- 
                Notifications
    You must be signed in to change notification settings 
- Fork 64
Replace map-based Multi signature with slice-based version for better performance #231
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
Conversation
This improves the test.Name func to be more flexible, allowing both odd and even number of entries to cover the case where we have a prefix name for the test that doesn't have a field label. This also updates the callsites accordingly.
(the code for this have been sitting on my local working copy for so long
that I decided to clean it up and get it integrated...)
This commit replaces the map[hotstuff.ID]T with []T in Multi type:
  type Multi[T Signature] []T
The rational for this change is that map-based data structures are
often much slower for small-medium sized sets/lists. It is often
faster to iterate over N entries than to do a map lookup. See the
benchmark results below.
This commit also makes the follow changes/updates:
- Replace Restore with NewMulti constructor
- Add NewMultiSorted constructor to ensure sorted signatures
  (signatures should be sorted by default, so I hope we can avoid
  using this constructor; we don't now, but if this becomes necessary,
  it is there and there are tests.)
- Updated ECDSA and EDDSA implementations to use slice-based Multi
- Updated the kauri tests to use the slice-based Multi
This change reduces memory overhead and improves iteration performance
by using a slice instead of a map for storing partial signatures.
goos: darwin
goarch: arm64
pkg: github.com/relab/hotstuff/security/crypto
cpu: Apple M2 Max
                  │ map-multisig.txt │         slice-multisig.txt          │
                  │      sec/op      │   sec/op     vs base                │
ForEach/n=10-12          92.62n ± 4%   19.54n ± 0%  -78.90% (p=0.000 n=20)
ForEach/n=100-12         637.8n ± 2%   212.9n ± 1%  -66.61% (p=0.000 n=20)
ForEach/n=1000-12        7.552µ ± 1%   2.026µ ± 0%  -73.16% (p=0.000 n=20)
ToBytes/n=10-12          1.699µ ± 0%   1.402µ ± 0%  -17.51% (p=0.000 n=20)
ToBytes/n=100-12         18.11µ ± 0%   14.18µ ± 1%  -21.71% (p=0.000 n=20)
ToBytes/n=1000-12        220.4µ ± 1%   157.4µ ± 1%  -28.61% (p=0.000 n=20)
geomean                  3.803µ        1.725µ       -54.64%
                  │ map-multisig.txt │          slice-multisig.txt           │
                  │       B/op       │     B/op      vs base                 │
ForEach/n=10-12         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=20) ¹
ForEach/n=100-12        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=20) ¹
ForEach/n=1000-12       0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=20) ¹
ToBytes/n=10-12       3.422Ki ± 0%     3.375Ki ± 0%  -1.37% (p=0.000 n=20)
ToBytes/n=100-12      40.22Ki ± 0%     39.81Ki ± 0%  -1.01% (p=0.000 n=20)
ToBytes/n=1000-12     438.9Ki ± 0%     434.9Ki ± 0%  -0.91% (p=0.000 n=20)
geomean                            ²                 -0.55%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
                  │ map-multisig.txt │          slice-multisig.txt          │
                  │    allocs/op     │  allocs/op   vs base                 │
ForEach/n=10-12         0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=20) ¹
ForEach/n=100-12        0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=20) ¹
ForEach/n=1000-12       0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=20) ¹
ToBytes/n=10-12         46.00 ± 0%      45.00 ± 0%  -2.17% (p=0.000 n=20)
ToBytes/n=100-12        412.0 ± 0%      411.0 ± 0%  -0.24% (p=0.000 n=20)
ToBytes/n=1000-12      4.020k ± 0%     4.019k ± 0%  -0.02% (p=0.000 n=20)
geomean                            ²                -0.41%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
    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.
Pull Request Overview
This PR replaces the map-based Multi signature type with a slice-based version to improve performance for small to medium-sized signature sets, showing significant performance improvements (54-79% faster) based on benchmark results.
- Replaced map[hotstuff.ID]Twith[]Tin theMultitype definition
- Added comprehensive test coverage for multi-signature functionality
- Updated ECDSA and EDDSA implementations to use the new slice-based approach
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description | 
|---|---|
| security/crypto/multisignature.go | Core change replacing map with slice and updating all Multi methods | 
| security/crypto/multisignature_test.go | New comprehensive test file for multi-signature functionality | 
| security/crypto/ecdsa.go | Updated ECDSA implementation to use slice-based Multi | 
| security/crypto/eddsa.go | Updated EDDSA implementation to use slice-based Multi | 
| internal/proto/hotstuffpb/convert.go | Updated to use NewMulti instead of deprecated Restore | 
| internal/test/name.go | Enhanced Name function with better API and field-value pair handling | 
| protocol/comm/kauri/kauri_test.go | Updated tests to use new Multi signature creation pattern | 
| twins/network_test.go | Minor test name generation update | 
| protocol/synchronizer/timeout_collector_test.go | Minor test name generation update | 
| internal/orchestration/orchestration_test.go | Minor test name generation update | 
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
(The code for this has been sitting on my local working copy for so long that I decided to clean it up and get it integrated...)
This PR replaces the
map[hotstuff.ID]Twith[]Tin theMultitype:The rationale for this change is that map-based data structures are often much slower for small to medium-sized sets/lists. It is often faster to iterate over N entries than to do a map lookup. See the benchmark results below.
This PR also makes the following changes/updates:
This change reduces memory overhead and improves iteration performance by using a slice instead of a map for storing partial signatures.