Skip to content

Conversation

@meling
Copy link
Member

@meling meling commented Oct 19, 2025

(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]T with []T in the Multi type:

  type Multi[T Signature] []T

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:

  • Add comprehensive tests for the multi-signature functionality
  • Replace Restore with NewMulti constructor
  • Add NewMultiSorted constructor to ensure sorted signatures (not used now, but it is available should the need arise)
  • 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

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
@meling meling requested a review from Copilot October 19, 2025 09:08
Copy link
Contributor

Copilot AI left a 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]T with []T in the Multi type 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.

@meling meling merged commit 7853370 into package-restructuring Oct 19, 2025
@meling meling deleted the multisignature-improvement branch October 19, 2025 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants