perf(weed/topology): preallocate the heartbeat volume conversion slice (#10607)

* test(weed/topology): benchmark the per-heartbeat volume sync

A volume server re-sends its entire volume list every VolumePulsePeriod, so
SyncDataNodeRegistration is the master's steady-state per-server cost. Give it
a benchmark so allocation regressions show up.

* perf(weed/topology): preallocate the heartbeat volume conversion slice

The slice grows to one entry per volume on the data node, so at 100k volumes
the doubling copies allocate 60MB of garbage per heartbeat. The final length is
known up front.

BenchmarkSyncDataNodeRegistration/100000Volumes  199670102 B/op -> 137725872 B/op
This commit is contained in:
Chris Lu
2026-08-07 00:21:16 -07:00
committed by GitHub
parent 2ff3dda7cd
commit 3fce1a938d
2 changed files with 53 additions and 1 deletions
+1 -1
View File
@@ -587,7 +587,7 @@ func (t *Topology) ListDCAndRacks() (dcs map[NodeId][]NodeId) {
func (t *Topology) SyncDataNodeRegistration(volumes []*master_pb.VolumeInformationMessage, dn *DataNode) (newVolumes, deletedVolumes []storage.VolumeInfo) {
// convert into in memory struct storage.VolumeInfo
var volumeInfos []storage.VolumeInfo
volumeInfos := make([]storage.VolumeInfo, 0, len(volumes))
for _, v := range volumes {
if vi, err := storage.NewVolumeInfo(v); err == nil {
volumeInfos = append(volumeInfos, vi)
@@ -0,0 +1,52 @@
package topology
import (
"fmt"
"testing"
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
)
func benchHeartbeatMessages(count int) []*master_pb.VolumeInformationMessage {
messages := make([]*master_pb.VolumeInformationMessage, 0, count)
for i := 0; i < count; i++ {
messages = append(messages, &master_pb.VolumeInformationMessage{
Id: uint32(i),
Size: 1024 * 1024,
Collection: "benchcollection",
FileCount: 100,
DeleteCount: 1,
DeletedByteCount: 1024,
ReplicaPlacement: 0,
Version: 3,
CompactRevision: 1,
ModifiedAtSecond: 1700000000,
})
}
return messages
}
// A volume server re-sends its whole volume list on every heartbeat, so this is
// the master's steady-state cost per volume server every VolumePulsePeriod.
func benchSyncDataNodeRegistration(b *testing.B, count int) {
topo := NewTopology("bench", nil, 32*1024*1024*1024, 5, false)
dn := topo.GetOrCreateDataCenter("dc1").GetOrCreateRack("rack1").
GetOrCreateDataNode("127.0.0.1", 8080, 18080, "", "", map[string]uint32{"": uint32(count) * 2})
topo.SyncDataNodeRegistration(benchHeartbeatMessages(count), dn)
messages := benchHeartbeatMessages(count)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
topo.SyncDataNodeRegistration(messages, dn)
}
}
func BenchmarkSyncDataNodeRegistration(b *testing.B) {
for _, count := range []int{1000, 100000} {
b.Run(fmt.Sprintf("%dVolumes", count), func(b *testing.B) {
benchSyncDataNodeRegistration(b, count)
})
}
}