frostfs-node/pkg/services/tree/bench_test.go
Evgenii Stratonikov 8cbf9a0b44
All checks were successful
DCO action / DCO (pull_request) Successful in 2m13s
Build / Build Components (1.20) (pull_request) Successful in 2m36s
Vulncheck / Vulncheck (pull_request) Successful in 3m1s
Build / Build Components (1.21) (pull_request) Successful in 4m7s
Tests and linters / Staticcheck (pull_request) Successful in 4m1s
Tests and linters / Tests (1.20) (pull_request) Successful in 6m13s
Tests and linters / Lint (pull_request) Successful in 7m12s
Tests and linters / Tests with -race (pull_request) Successful in 7m27s
Tests and linters / Tests (1.21) (pull_request) Successful in 2m30s
[#957] treesvc: Use sorting when number of items is small
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-02-06 14:14:50 +03:00

121 lines
3.2 KiB
Go

package tree
import (
"context"
"testing"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama"
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"github.com/google/uuid"
"google.golang.org/grpc"
)
func BenchmarkGetSubTree(b *testing.B) {
const count = simpleHeapFallbackThreshold
d := pilorama.CIDDescriptor{CID: cidtest.ID(), Size: 1}
treeID := "sometree"
body := &GetSubTreeRequest_Body{
TreeId: treeID,
RootId: pilorama.RootID,
Depth: 2,
OrderBy: &GetSubTreeRequest_Body_Order{
Direction: GetSubTreeRequest_Body_Order_Asc,
},
}
p := newBenchPilorama(count)
b.Run("latency", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
acc := benchServer{start: time.Now(), errIndex: count + 1}
err := getSubTree(context.Background(), &acc, d.CID, body, p)
if err != nil {
b.Fatalf("Error: %v, expected: %d, got %d", err, count, acc.seen)
}
b.ReportMetric(float64(uint64(acc.first)/uint64(b.N)), "ns/op")
}
})
b.Run("total time", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
acc := benchServer{start: time.Now(), errIndex: count + 1}
err := getSubTree(context.Background(), &acc, d.CID, body, p)
if err != nil {
b.Fatalf("Error: %v, expected: %d, got %d", err, count, acc.seen)
}
}
})
}
// benchPilorama represents flat pilorama, i.e. tree of height 2 with many child nodes.
type benchPilorama struct {
pilorama.Forest // Satisfy interface.
nodes []pilorama.NodeInfo
}
func newBenchPilorama(size int) *benchPilorama {
nodes := make([]pilorama.NodeInfo, 0, size)
for i := 1; i <= size; i++ { // Start with 1 to avoid intersecting with RootID = 0.
nodes = append(nodes, pilorama.NodeInfo{
ParentID: pilorama.RootID,
ID: pilorama.Node(i),
Meta: pilorama.Meta{
Items: []pilorama.KeyValue{{
Key: pilorama.AttributeFilename,
Value: []byte(uuid.New().String()),
}},
},
})
}
return &benchPilorama{nodes: nodes}
}
func (p *benchPilorama) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]pilorama.NodeInfo, error) {
switch nodeID {
case pilorama.RootID:
result := make([]pilorama.NodeInfo, len(p.nodes))
copy(result, p.nodes)
return result, nil
default:
panic("unexpected")
}
}
func (p *benchPilorama) TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID string, root pilorama.Node) (pilorama.Meta, pilorama.Node, error) {
if root == pilorama.RootID {
return pilorama.Meta{}, pilorama.RootID, nil
}
return p.nodes[root-1].Meta, p.nodes[root-1].ParentID, nil
}
type benchServer struct {
grpc.ServerStream // to satisfy the interface
start time.Time
first time.Duration
seen int
errIndex int
}
var _ TreeService_GetSubTreeServer = (*benchServer)(nil)
func (s *benchServer) Send(r *GetSubTreeResponse) error {
if s.seen == 1 {
s.first = time.Since(s.start)
}
s.seen++
if s.errIndex >= 0 {
if s.seen == s.errIndex+1 {
return errSubTreeSend
}
if s.errIndex >= 0 && s.seen > s.errIndex {
return errSubTreeSendAfterError
}
}
return nil
}