perf(weed/topology): gather a node's volumes into one slice (#10617)

* perf(weed/topology): preallocate the node's volume concatenation

A node's volumes are gathered per disk and concatenated into a slice grown from
nil, so a server with several disks reallocates and copies its way up. The
writable-volume refresh loop does this for every node every few seconds.

BenchmarkDataNodeGetVolumes/8Disks  322551844 B/op -> 121602326 B/op

* perf(weed/topology): fill one slice across a node's disks

Each disk built its own right-sized copy of its volumes, and the node then
copied all of them again into the combined slice. Appending into the caller's
slice makes it one allocation whatever the disk count, which halves even the
single-disk case.

BenchmarkDataNodeGetVolumes
  1Disks  121602326 B/op 2 allocs/op -> 60801314 B/op 1 allocs/op
  8Disks  121602326 B/op 9 allocs/op -> 60801024 B/op 1 allocs/op
This commit is contained in:
Chris Lu
2026-08-07 01:26:54 -07:00
committed by GitHub
parent 228e850da1
commit cce3bab0e2
2 changed files with 17 additions and 8 deletions
+7 -3
View File
@@ -200,11 +200,15 @@ func (dn *DataNode) AdjustDiskUsageBytes(diskTotalBytes, diskFreeBytes map[strin
func (dn *DataNode) GetVolumes() (ret []storage.VolumeInfo) {
dn.RLock()
defer dn.RUnlock()
total := 0
for _, c := range dn.children {
disk := c.(*Disk)
ret = append(ret, disk.GetVolumes()...)
total += c.(*Disk).VolumeCount()
}
ret = make([]storage.VolumeInfo, 0, total)
for _, c := range dn.children {
ret = c.(*Disk).AppendVolumes(ret)
}
dn.RUnlock()
return ret
}
+10 -5
View File
@@ -202,14 +202,19 @@ func (d *Disk) doAddOrUpdateVolume(v storage.VolumeInfo) (isNew, isChanged bool)
return
}
func (d *Disk) GetVolumes() (ret []storage.VolumeInfo) {
func (d *Disk) GetVolumes() []storage.VolumeInfo {
return d.AppendVolumes(make([]storage.VolumeInfo, 0, d.VolumeCount()))
}
// AppendVolumes appends the disk's volumes to dst, so a caller gathering
// several disks fills one slice instead of concatenating a copy per disk.
func (d *Disk) AppendVolumes(dst []storage.VolumeInfo) []storage.VolumeInfo {
d.RLock()
ret = make([]storage.VolumeInfo, 0, len(d.volumes))
defer d.RUnlock()
for _, v := range d.volumes {
ret = append(ret, v)
dst = append(dst, v)
}
d.RUnlock()
return ret
return dst
}
func (d *Disk) VolumeCount() int {