Skip to content
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

test: extends computeRoots benchmark to cover larger square sizes and nmt tree #304

Merged
merged 11 commits into from
Mar 7, 2024
53 changes: 52 additions & 1 deletion datasquare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package rsmt2d
import (
"crypto/sha256"
"fmt"
"math"
"reflect"
"testing"

"github.com/celestiaorg/merkletree"
"github.com/celestiaorg/nmt"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -401,7 +403,7 @@ func Test_setColSlice(t *testing.T) {
}
}

func BenchmarkEDSRoots(b *testing.B) {
func BenchmarkEDSRootsWithDefaultTree(b *testing.B) {
for i := 32; i < 513; i *= 2 {
square, err := newDataSquare(genRandDS(i*2, int(shareSize)), NewDefaultTree, shareSize)
if err != nil {
Expand All @@ -420,6 +422,55 @@ func BenchmarkEDSRoots(b *testing.B) {
}
}

func BenchmarkEDSRootsWithErasuredNMT(b *testing.B) {
const mebibyte = 1024 * 1024 // bytes
ODSSizeByteUpperBound := 512 * mebibyte // converting 512 MiB to bytes
totalNumberOfShares := float64(ODSSizeByteUpperBound) / shareSize
// the closest power of 2 of the square root of
// the total number of shares
nearestPowerOf2ODSSize := math.Pow(2, math.Ceil(math.Log2(math.Sqrt(
totalNumberOfShares))))
namespaceIDSize := 29

for squareSize := 32; squareSize <= int(nearestPowerOf2ODSSize); squareSize *= 2 {
// number of shares in the original data square's row/column
odsSize := squareSize
// number of shares in the extended data square's row/column
edsSize := 2 * odsSize
// generate an EDS with edsSize X edsSize dimensions in terms of shares.
// the generated EDS does not conform to celestia-app specs in terms
// of namespace version, also no erasure encoding takes place
// yet none of these should impact the benchmarking
ds := genRandSortedDS(edsSize, shareSize, namespaceIDSize)

// a tree constructor for erasured nmt
treeConstructor := newErasuredNamespacedMerkleTreeConstructor(uint64(edsSize),
nmt.NamespaceIDSize(namespaceIDSize), nmt.IgnoreMaxNamespace(true),
nmt.InitialCapacity(odsSize*2))

square, err := newDataSquare(ds, treeConstructor, shareSize)
if err != nil {
b.Errorf("Failure to create square of size %d: %s", odsSize, err)
}
// the total size of the ODS in MiB
odsSizeMiBytes := odsSize * odsSize * shareSize / mebibyte
// the total size of the EDS in MiB
edsSizeMiBytes := 4 * odsSizeMiBytes
b.Run(
fmt.Sprintf("%dx%dx%d ODS=%dMB, EDS=%dMB", odsSize, odsSize,
int(square.chunkSize),
odsSizeMiBytes, edsSizeMiBytes),
func(b *testing.B) {
for n := 0; n < b.N; n++ {
square.resetRoots()
err := square.computeRoots()
assert.NoError(b, err)
}
},
)
}
}

func computeRowProof(ds *dataSquare, x uint, y uint) ([]byte, [][]byte, uint, uint, error) {
tree := ds.createTreeFn(Row, x)
data := ds.row(x)
Expand Down
2 changes: 1 addition & 1 deletion extendeddatacrossword_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ func createTestEdsWithNMT(t *testing.T, codec Codec, shareSize, namespaceSize in
edsWidth := 4 // number of shares per row/column in the extended data square
odsWidth := edsWidth / 2 // number of shares per row/column in the original data square

eds, err := ComputeExtendedDataSquare(shares, codec, newConstructor(uint64(odsWidth), nmt.NamespaceIDSize(namespaceSize)))
eds, err := ComputeExtendedDataSquare(shares, codec, newErasuredNamespacedMerkleTreeConstructor(uint64(odsWidth), nmt.NamespaceIDSize(namespaceSize)))
require.NoError(t, err)

return eds
Expand Down
13 changes: 13 additions & 0 deletions extendeddatasquare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"reflect"
"sort"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -347,6 +348,18 @@ func genRandDS(width int, chunkSize int) [][]byte {
return ds
}

func genRandSortedDS(width int, chunkSize int, namespaceSize int) [][]byte {
ds := genRandDS(width, chunkSize)

// Sort the shares in the square based on their namespace
sort.Slice(ds, func(i, j int) bool {
// Compare only the first namespaceSize bytes
return bytes.Compare(ds[i][:namespaceSize], ds[j][:namespaceSize]) < 0
})

return ds
}

// TestFlattened_EDS tests that eds.Flattened() returns all the shares in the
// EDS. This function has the `_EDS` suffix to avoid a name collision with the
// TestFlattened.
Expand Down
8 changes: 5 additions & 3 deletions nmtwrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ type nmtTree interface {
// with an underlying NMT of namespace size `NamespaceSize` and with
// `ignoreMaxNamespace=true`. axisIndex is the index of the row or column that
// this tree is committing to. squareSize must be greater than zero.
func newErasuredNamespacedMerkleTree(squareSize uint64, axisIndex uint, options ...nmt.Option) erasuredNamespacedMerkleTree {
func newErasuredNamespacedMerkleTree(squareSize uint64, axisIndex uint,
options ...nmt.Option,
) erasuredNamespacedMerkleTree {
if squareSize == 0 {
panic("cannot create a erasuredNamespacedMerkleTree of squareSize == 0")
}
Expand All @@ -73,10 +75,10 @@ type constructor struct {
opts []nmt.Option
}

// newConstructor creates a tree constructor function as required by rsmt2d to
// newErasuredNamespacedMerkleTreeConstructor creates a tree constructor function as required by rsmt2d to
// calculate the data root. It creates that tree using the
// erasuredNamespacedMerkleTree.
func newConstructor(squareSize uint64, opts ...nmt.Option) TreeConstructorFn {
func newErasuredNamespacedMerkleTreeConstructor(squareSize uint64, opts ...nmt.Option) TreeConstructorFn {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit unrelated to this PR, but do we have the wrapper in both places, here and in app? https://github.com/celestiaorg/celestia-app/tree/main/pkg/wrapper

Copy link
Collaborator Author

@staheri14 staheri14 Mar 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! Yes, we have, I did it in the past (inside the nmtwrapper_test file) to conduct some tests in the rsmt2d side without making circular dependency to the app.
PS: There is also an open issue #248 about moving the wrapper completely to the rsmt2d repo.

return constructor{
squareSize: squareSize,
opts: opts,
Expand Down
Loading