2023-03-03 09:33:08 +00:00
|
|
|
package blobstor
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/memstore"
|
2023-03-20 14:10:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
|
2023-03-03 09:33:08 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2023-04-11 17:51:58 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2023-03-03 09:33:08 +00:00
|
|
|
)
|
|
|
|
|
2023-04-11 17:20:32 +00:00
|
|
|
type storage struct {
|
|
|
|
desc string
|
|
|
|
create func(string) common.Storage
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s storage) open(b *testing.B) common.Storage {
|
|
|
|
dir, err := os.MkdirTemp(os.TempDir(), s.desc)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatalf("creating %s root path: %v", s.desc, err)
|
|
|
|
}
|
|
|
|
st := s.create(dir)
|
|
|
|
|
|
|
|
require.NoError(b, st.Open(false))
|
|
|
|
require.NoError(b, st.Init())
|
|
|
|
|
|
|
|
b.Cleanup(func() {
|
|
|
|
require.NoError(b, st.Close())
|
|
|
|
require.NoError(b, os.RemoveAll(dir))
|
|
|
|
})
|
|
|
|
|
|
|
|
return st
|
|
|
|
}
|
|
|
|
|
2023-03-03 09:33:08 +00:00
|
|
|
// The storages to benchmark. Each storage has a description and a function which returns the actual
|
|
|
|
// storage along with a cleanup function.
|
2023-04-11 17:20:32 +00:00
|
|
|
var storages = []storage{
|
2023-03-03 09:33:08 +00:00
|
|
|
{
|
|
|
|
desc: "memstore",
|
2023-04-11 17:20:32 +00:00
|
|
|
create: func(string) common.Storage {
|
|
|
|
return memstore.New()
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "fstree_nosync",
|
2023-04-11 17:20:32 +00:00
|
|
|
create: func(dir string) common.Storage {
|
2023-03-03 09:33:08 +00:00
|
|
|
return fstree.New(
|
|
|
|
fstree.WithPath(dir),
|
|
|
|
fstree.WithDepth(2),
|
|
|
|
fstree.WithDirNameLen(2),
|
|
|
|
fstree.WithNoSync(true),
|
2023-04-11 17:20:32 +00:00
|
|
|
)
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "fstree",
|
2023-04-11 17:20:32 +00:00
|
|
|
create: func(dir string) common.Storage {
|
2023-03-03 09:33:08 +00:00
|
|
|
return fstree.New(
|
|
|
|
fstree.WithPath(dir),
|
|
|
|
fstree.WithDepth(2),
|
|
|
|
fstree.WithDirNameLen(2),
|
2023-04-11 17:20:32 +00:00
|
|
|
)
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "blobovniczatree",
|
2023-04-11 17:20:32 +00:00
|
|
|
create: func(dir string) common.Storage {
|
2023-03-03 09:33:08 +00:00
|
|
|
return blobovniczatree.NewBlobovniczaTree(
|
|
|
|
blobovniczatree.WithRootPath(dir),
|
2023-04-11 17:20:32 +00:00
|
|
|
)
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkSubstorageReadPerf(b *testing.B) {
|
|
|
|
readTests := []struct {
|
|
|
|
desc string
|
|
|
|
size int
|
2023-03-20 14:10:26 +00:00
|
|
|
objGen func() testutil.ObjectGenerator
|
|
|
|
addrGen func() testutil.AddressGenerator
|
2023-03-03 09:33:08 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
desc: "seq100",
|
|
|
|
size: 10000,
|
2023-03-20 14:10:26 +00:00
|
|
|
objGen: func() testutil.ObjectGenerator { return &testutil.SeqObjGenerator{ObjSize: 100} },
|
|
|
|
addrGen: func() testutil.AddressGenerator { return &testutil.SeqAddrGenerator{MaxID: 100} },
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "rand100",
|
|
|
|
size: 10000,
|
2023-03-20 14:10:26 +00:00
|
|
|
objGen: func() testutil.ObjectGenerator { return &testutil.SeqObjGenerator{ObjSize: 100} },
|
|
|
|
addrGen: func() testutil.AddressGenerator { return testutil.RandAddrGenerator(10000) },
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range readTests {
|
|
|
|
for _, stEntry := range storages {
|
|
|
|
b.Run(fmt.Sprintf("%s-%s", stEntry.desc, tt.desc), func(b *testing.B) {
|
|
|
|
objGen := tt.objGen()
|
2023-04-11 17:20:32 +00:00
|
|
|
st := stEntry.open(b)
|
2023-03-03 09:33:08 +00:00
|
|
|
|
|
|
|
// Fill database
|
2023-04-11 17:51:58 +00:00
|
|
|
var errG errgroup.Group
|
2023-03-03 09:33:08 +00:00
|
|
|
for i := 0; i < tt.size; i++ {
|
|
|
|
obj := objGen.Next()
|
2023-03-28 14:16:03 +00:00
|
|
|
addr := testutil.AddressFromObject(b, obj)
|
2023-04-11 17:51:58 +00:00
|
|
|
errG.Go(func() error {
|
|
|
|
raw, err := obj.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("marshal: %v", err)
|
|
|
|
}
|
|
|
|
_, err = st.Put(common.PutPrm{
|
|
|
|
Address: addr,
|
|
|
|
RawData: raw,
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
2023-03-03 09:33:08 +00:00
|
|
|
}
|
2023-04-11 17:51:58 +00:00
|
|
|
require.NoError(b, errG.Wait())
|
2023-03-03 09:33:08 +00:00
|
|
|
|
|
|
|
// Benchmark reading
|
|
|
|
addrGen := tt.addrGen()
|
|
|
|
b.ResetTimer()
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
|
|
for pb.Next() {
|
|
|
|
_, err := st.Get(common.GetPrm{Address: addrGen.Next()})
|
|
|
|
require.NoError(b, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkSubstorageWritePerf(b *testing.B) {
|
|
|
|
generators := []struct {
|
|
|
|
desc string
|
2023-03-20 14:10:26 +00:00
|
|
|
create func() testutil.ObjectGenerator
|
2023-03-03 09:33:08 +00:00
|
|
|
}{
|
2023-03-20 14:10:26 +00:00
|
|
|
{desc: "rand10", create: func() testutil.ObjectGenerator { return &testutil.RandObjGenerator{ObjSize: 10} }},
|
|
|
|
{desc: "rand100", create: func() testutil.ObjectGenerator { return &testutil.RandObjGenerator{ObjSize: 100} }},
|
|
|
|
{desc: "rand1000", create: func() testutil.ObjectGenerator { return &testutil.RandObjGenerator{ObjSize: 1000} }},
|
|
|
|
{desc: "overwrite10", create: func() testutil.ObjectGenerator { return &testutil.OverwriteObjGenerator{ObjSize: 10, MaxObjects: 100} }},
|
|
|
|
{desc: "overwrite100", create: func() testutil.ObjectGenerator { return &testutil.OverwriteObjGenerator{ObjSize: 100, MaxObjects: 100} }},
|
|
|
|
{desc: "overwrite1000", create: func() testutil.ObjectGenerator {
|
|
|
|
return &testutil.OverwriteObjGenerator{ObjSize: 1000, MaxObjects: 100}
|
|
|
|
}},
|
2023-03-03 09:33:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, genEntry := range generators {
|
|
|
|
for _, stEntry := range storages {
|
|
|
|
b.Run(fmt.Sprintf("%s-%s", stEntry.desc, genEntry.desc), func(b *testing.B) {
|
|
|
|
gen := genEntry.create()
|
2023-04-11 17:20:32 +00:00
|
|
|
st := stEntry.open(b)
|
2023-03-03 09:33:08 +00:00
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
|
|
for pb.Next() {
|
|
|
|
obj := gen.Next()
|
2023-03-28 14:16:03 +00:00
|
|
|
addr := testutil.AddressFromObject(b, obj)
|
2023-03-03 09:33:08 +00:00
|
|
|
raw, err := obj.Marshal()
|
|
|
|
require.NoError(b, err)
|
|
|
|
if _, err := st.Put(common.PutPrm{
|
|
|
|
Address: addr,
|
|
|
|
RawData: raw,
|
|
|
|
}); err != nil {
|
|
|
|
b.Fatalf("writing entry: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkSubstorageIteratePerf(b *testing.B) {
|
|
|
|
iterateTests := []struct {
|
|
|
|
desc string
|
|
|
|
size int
|
2023-03-20 14:10:26 +00:00
|
|
|
objGen func() testutil.ObjectGenerator
|
2023-03-03 09:33:08 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
desc: "rand100",
|
|
|
|
size: 10000,
|
2023-03-20 14:10:26 +00:00
|
|
|
objGen: func() testutil.ObjectGenerator { return &testutil.RandObjGenerator{ObjSize: 100} },
|
2023-03-03 09:33:08 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range iterateTests {
|
|
|
|
for _, stEntry := range storages {
|
|
|
|
b.Run(fmt.Sprintf("%s-%s", stEntry.desc, tt.desc), func(b *testing.B) {
|
|
|
|
objGen := tt.objGen()
|
2023-04-11 17:20:32 +00:00
|
|
|
st := stEntry.open(b)
|
2023-03-03 09:33:08 +00:00
|
|
|
|
|
|
|
// Fill database
|
|
|
|
for i := 0; i < tt.size; i++ {
|
|
|
|
obj := objGen.Next()
|
2023-03-28 14:16:03 +00:00
|
|
|
addr := testutil.AddressFromObject(b, obj)
|
2023-03-03 09:33:08 +00:00
|
|
|
raw, err := obj.Marshal()
|
|
|
|
require.NoError(b, err)
|
|
|
|
if _, err := st.Put(common.PutPrm{
|
|
|
|
Address: addr,
|
|
|
|
RawData: raw,
|
|
|
|
}); err != nil {
|
|
|
|
b.Fatalf("writing entry: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Benchmark iterate
|
|
|
|
cnt := 0
|
|
|
|
b.ResetTimer()
|
|
|
|
_, err := st.Iterate(common.IteratePrm{
|
|
|
|
Handler: func(elem common.IterationElement) error {
|
|
|
|
cnt++
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(b, err)
|
|
|
|
require.Equal(b, tt.size, cnt)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|