frostfs-sdk-go/netmap/context.go
Evgenii Stratonikov d282cd094f
All checks were successful
DCO / DCO (pull_request) Successful in 23s
Code generation / Generate proto (pull_request) Successful in 1m27s
Tests and linters / Tests (pull_request) Successful in 1m27s
Tests and linters / Lint (pull_request) Successful in 2m35s
[#355] netmap: Cache price and capacity attributes
They are used by HRW sorting and it makes sense to store them separately
instead of iterating over all attributes each time we need them.
It also simplifies code: we already parse them in NodeInfo.readFromV2(),
so just save the result.

```
goos: linux
goarch: amd64
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap
cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
                                                              │     old     │                 new                 │
                                                              │   sec/op    │   sec/op     vs base                │
Netmap_ContainerNodes/REP_2-8                                   5.923µ ± 0%   5.209µ ± 1%  -12.05% (p=0.000 n=10)
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   5.931µ ± 7%   5.088µ ± 1%  -14.22% (p=0.000 n=10)
geomean                                                         5.927µ        5.148µ       -13.14%

                                                              │     old      │                 new                 │
                                                              │     B/op     │     B/op      vs base               │
Netmap_ContainerNodes/REP_2-8                                   7.609Ki ± 0%   8.172Ki ± 0%  +7.39% (p=0.000 n=10)
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   7.031Ki ± 0%   7.469Ki ± 0%  +6.22% (p=0.000 n=10)
geomean                                                         7.315Ki        7.812Ki       +6.81%

                                                              │    old     │                 new                 │
                                                              │ allocs/op  │ allocs/op   vs base                 │
Netmap_ContainerNodes/REP_2-8                                   77.00 ± 0%   77.00 ± 0%       ~ (p=1.000 n=10) ¹
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   77.00 ± 0%   77.00 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                         77.00        77.00       +0.00%
¹ all samples are equal
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 17:47:08 +03:00

107 lines
2.6 KiB
Go

package netmap
import (
"errors"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/netmap"
"git.frostfs.info/TrueCloudLab/hrw"
)
// context of a placement build process.
type context struct {
// network map to operate on
netMap NetMap
// cache of processed filters
processedFilters map[string]*netmap.Filter
// cache of processed selectors
processedSelectors map[string]*netmap.Selector
// stores results of selector processing
selections map[string][]nodes
// cache of parsed numeric values
numCache map[string]uint64
hrwSeed []byte
// hrw.Hash of hrwSeed
hrwSeedHash uint64
// weightFunc is a weighting function for determining node priority
// which combines low price and high performance
weightFunc weightFunc
// container backup factor
cbf uint32
// nodes already used in previous selections, which is needed when the placement
// policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent
// base selections.
usedNodes map[uint64]bool
// If true, returns an error when netmap does not contain enough nodes for selection.
// By default best effort is taken.
strict bool
}
// Various validation errors.
var (
errInvalidFilterName = errors.New("filter name is invalid")
errInvalidNumber = errors.New("invalid number")
errInvalidFilterOp = errors.New("invalid filter operation")
errFilterNotFound = errors.New("filter not found")
errNonEmptyFilters = errors.New("simple filter contains sub-filters")
errNotEnoughNodes = errors.New("not enough nodes to SELECT from")
errUnnamedTopFilter = errors.New("unnamed top-level filter")
)
// newContext returns initialized context.
func newContext(nm NetMap) *context {
return &context{
netMap: nm,
processedFilters: make(map[string]*netmap.Filter),
processedSelectors: make(map[string]*netmap.Selector),
selections: make(map[string][]nodes),
numCache: make(map[string]uint64),
weightFunc: defaultWeightFunc(nm.nodes),
usedNodes: make(map[uint64]bool),
}
}
func (c *context) setPivot(pivot []byte) {
if len(pivot) != 0 {
c.hrwSeed = pivot
c.hrwSeedHash = hrw.Hash(pivot)
}
}
func (c *context) setCBF(cbf uint32) {
if cbf == 0 {
c.cbf = 3
} else {
c.cbf = cbf
}
}
func (c *context) addUsedNodes(ns ...NodeInfo) {
for _, n := range ns {
c.usedNodes[n.hash] = true
}
}
func defaultWeightFunc(ns nodes) weightFunc {
mean := newMeanAgg()
minV := newMinAgg()
for i := range ns {
mean.Add(float64(ns[i].capacity))
minV.Add(float64(ns[i].price))
}
return newWeightFunc(
newSigmoidNorm(mean.Compute()),
newReverseMinNorm(minV.Compute()))
}