forked from TrueCloudLab/frostfs-api-go
[#376] netmap: Make attributes a non-pointer slice
The speed of copying (which is done regulary for e.g. subnet changes) is less, however it isn't on the hot path and the absolute time difference is insignificant. ``` name old time/op new time/op delta NodeAttributes-8 96.2ns ± 1% 158.3ns ± 1% +64.61% (p=0.000 n=10+10) name old alloc/op new alloc/op delta NodeAttributes-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) name old allocs/op new allocs/op delta NodeAttributes-8 2.00 ± 0% 2.00 ± 0% ~ (all equal) ``` Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
2f0eee96fc
commit
6f4908edc2
6 changed files with 82 additions and 40 deletions
|
@ -101,12 +101,10 @@ func WriteSubnetInfo(node *NodeInfo, info NodeSubnetInfo) {
|
|||
}
|
||||
|
||||
if !presented {
|
||||
var attr Attribute
|
||||
|
||||
attr.SetKey(key)
|
||||
attr.SetValue(val)
|
||||
|
||||
attrs = append(attrs, &attr)
|
||||
index := len(attrs)
|
||||
attrs = append(attrs, Attribute{})
|
||||
attrs[index].SetKey(key)
|
||||
attrs[index].SetValue(val)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,12 +206,10 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
|||
}
|
||||
|
||||
// zero subnet should be clearly removed with False value
|
||||
var attr Attribute
|
||||
|
||||
attr.SetKey(subnetAttributeKey(&id))
|
||||
attr.SetValue(attrSubnetValExit)
|
||||
|
||||
attrs = append(attrs, &attr)
|
||||
index := len(attrs)
|
||||
attrs = append(attrs, Attribute{})
|
||||
attrs[index].SetKey(subnetAttributeKey(&id))
|
||||
attrs[index].SetValue(attrSubnetValExit)
|
||||
} else {
|
||||
entries++
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||
netmaptest "github.com/nspcc-dev/neofs-api-go/v2/netmap/test"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -17,6 +18,57 @@ func assertSubnetAttrKey(t *testing.T, attr *netmap.Attribute, num uint32) {
|
|||
require.Equal(t, subnetAttrKey(strconv.FormatUint(uint64(num), 10)), attr.GetKey())
|
||||
}
|
||||
|
||||
func BenchmarkNodeAttributes(b *testing.B) {
|
||||
const size = 50
|
||||
|
||||
id := new(refs.SubnetID)
|
||||
id.SetValue(12)
|
||||
|
||||
attrs := make([]netmap.Attribute, size)
|
||||
for i := range attrs {
|
||||
if i == size/2 {
|
||||
attrs[i] = *netmaptest.GenerateAttribute(false)
|
||||
} else {
|
||||
data, err := id.MarshalText()
|
||||
require.NoError(b, err)
|
||||
|
||||
attrs[i].SetKey(subnetAttrKey(string(data)))
|
||||
attrs[i].SetValue("True")
|
||||
}
|
||||
}
|
||||
|
||||
var info netmap.NodeSubnetInfo
|
||||
info.SetID(id)
|
||||
info.SetEntryFlag(false)
|
||||
|
||||
node := new(netmap.NodeInfo)
|
||||
|
||||
// When using a single slice `StartTimer` overhead is comparable to the
|
||||
// function execution time, so we reduce this cost by updating slices in groups.
|
||||
const cacheSize = 1000
|
||||
a := make([][]netmap.Attribute, cacheSize)
|
||||
for i := range a {
|
||||
a[i] = make([]netmap.Attribute, size)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if i%cacheSize == 0 {
|
||||
b.StopTimer()
|
||||
for j := range a {
|
||||
copy(a[j], attrs)
|
||||
}
|
||||
b.StartTimer()
|
||||
}
|
||||
node.SetAttributes(a[i%cacheSize])
|
||||
netmap.WriteSubnetInfo(node, info)
|
||||
if len(node.GetAttributes())+1 != len(attrs) {
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSubnetInfo(t *testing.T) {
|
||||
t.Run("entry", func(t *testing.T) {
|
||||
t.Run("zero subnet", func(t *testing.T) {
|
||||
|
@ -40,7 +92,7 @@ func TestWriteSubnetInfo(t *testing.T) {
|
|||
attrs = node.GetAttributes()
|
||||
require.Len(t, attrs, 1)
|
||||
|
||||
attr := attrs[0]
|
||||
attr := &attrs[0]
|
||||
assertSubnetAttrKey(t, attr, 0)
|
||||
require.Equal(t, "False", attr.GetValue())
|
||||
|
||||
|
@ -76,7 +128,7 @@ func TestWriteSubnetInfo(t *testing.T) {
|
|||
attrs := node.GetAttributes()
|
||||
require.Len(t, attrs, 1)
|
||||
|
||||
attr := attrs[0]
|
||||
attr := &attrs[0]
|
||||
assertSubnetAttrKey(t, attr, num)
|
||||
require.Equal(t, "True", attr.GetValue())
|
||||
|
||||
|
@ -128,7 +180,7 @@ func TestSubnets(t *testing.T) {
|
|||
attrExit.SetKey(subnetAttrKey(strconv.FormatUint(numExit, 10)))
|
||||
attrExit.SetValue("False")
|
||||
|
||||
attrs := []*netmap.Attribute{&attrEntry, &attrEntry}
|
||||
attrs := []netmap.Attribute{attrEntry, attrEntry}
|
||||
|
||||
node.SetAttributes(attrs)
|
||||
|
||||
|
@ -157,7 +209,7 @@ func TestSubnets(t *testing.T) {
|
|||
assertErr := func(attr netmap.Attribute) {
|
||||
var node netmap.NodeInfo
|
||||
|
||||
node.SetAttributes([]*netmap.Attribute{&attr})
|
||||
node.SetAttributes([]netmap.Attribute{attr})
|
||||
|
||||
require.Error(t, netmap.IterateSubnets(&node, func(refs.SubnetID) error {
|
||||
return nil
|
||||
|
@ -200,7 +252,7 @@ func TestSubnets(t *testing.T) {
|
|||
attr.SetKey(subnetAttrKey("321"))
|
||||
attr.SetValue("True")
|
||||
|
||||
attrs := []*netmap.Attribute{&attr}
|
||||
attrs := []netmap.Attribute{attr}
|
||||
node.SetAttributes(attrs)
|
||||
|
||||
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
|
||||
|
@ -237,7 +289,7 @@ func TestSubnets(t *testing.T) {
|
|||
attr.SetKey(subnetAttrKey("99"))
|
||||
attr.SetValue("True")
|
||||
|
||||
attrs := []*netmap.Attribute{&attr}
|
||||
attrs := []netmap.Attribute{attr}
|
||||
node.SetAttributes(attrs)
|
||||
|
||||
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
|
||||
|
@ -257,7 +309,7 @@ func TestSubnets(t *testing.T) {
|
|||
t.Run("all", func(t *testing.T) {
|
||||
var (
|
||||
node netmap.NodeInfo
|
||||
attrs []*netmap.Attribute
|
||||
attrs []netmap.Attribute
|
||||
)
|
||||
|
||||
// enter to some non-zero subnet so that zero is not the only one
|
||||
|
@ -267,7 +319,7 @@ func TestSubnets(t *testing.T) {
|
|||
attr.SetKey(subnetAttrKey(strconv.Itoa(i)))
|
||||
attr.SetValue("True")
|
||||
|
||||
attrs = append(attrs, &attr)
|
||||
attrs = append(attrs, attr)
|
||||
}
|
||||
|
||||
node.SetAttributes(attrs)
|
||||
|
@ -293,7 +345,7 @@ func TestSubnets(t *testing.T) {
|
|||
attrOther.SetKey(subnetAttrKey("1"))
|
||||
attrOther.SetValue("True")
|
||||
|
||||
node.SetAttributes([]*netmap.Attribute{&attrZero, &attrOther})
|
||||
node.SetAttributes([]netmap.Attribute{attrZero, attrOther})
|
||||
|
||||
calledCount := 0
|
||||
|
||||
|
|
|
@ -297,7 +297,7 @@ func (a *Attribute) FromGRPCMessage(m grpc.Message) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func AttributesToGRPC(as []*Attribute) (res []*netmap.NodeInfo_Attribute) {
|
||||
func AttributesToGRPC(as []Attribute) (res []*netmap.NodeInfo_Attribute) {
|
||||
if as != nil {
|
||||
res = make([]*netmap.NodeInfo_Attribute, 0, len(as))
|
||||
|
||||
|
@ -309,23 +309,17 @@ func AttributesToGRPC(as []*Attribute) (res []*netmap.NodeInfo_Attribute) {
|
|||
return
|
||||
}
|
||||
|
||||
func AttributesFromGRPC(as []*netmap.NodeInfo_Attribute) (res []*Attribute, err error) {
|
||||
func AttributesFromGRPC(as []*netmap.NodeInfo_Attribute) (res []Attribute, err error) {
|
||||
if as != nil {
|
||||
res = make([]*Attribute, 0, len(as))
|
||||
res = make([]Attribute, len(as))
|
||||
|
||||
for i := range as {
|
||||
var a *Attribute
|
||||
|
||||
if as[i] != nil {
|
||||
a = new(Attribute)
|
||||
|
||||
err = a.FromGRPCMessage(as[i])
|
||||
err = res[i].FromGRPCMessage(as[i])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res = append(res, a)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -383,7 +383,7 @@ func (ni *NodeInfo) StableMarshal(buf []byte) ([]byte, error) {
|
|||
offset += n
|
||||
|
||||
for i := range ni.attributes {
|
||||
n, err = protoutil.NestedStructureMarshal(attributesNodeInfoField, buf[offset:], ni.attributes[i])
|
||||
n, err = protoutil.NestedStructureMarshal(attributesNodeInfoField, buf[offset:], &ni.attributes[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -408,7 +408,7 @@ func (ni *NodeInfo) StableSize() (size int) {
|
|||
size += protoutil.RepeatedStringSize(addressNodeInfoField, ni.addresses)
|
||||
|
||||
for i := range ni.attributes {
|
||||
size += protoutil.NestedStructureSize(attributesNodeInfoField, ni.attributes[i])
|
||||
size += protoutil.NestedStructureSize(attributesNodeInfoField, &ni.attributes[i])
|
||||
}
|
||||
|
||||
size += protoutil.EnumSize(stateNodeInfoField, int32(ni.state))
|
||||
|
|
|
@ -119,13 +119,13 @@ func GenerateAttribute(empty bool) *netmap.Attribute {
|
|||
return m
|
||||
}
|
||||
|
||||
func GenerateAttributes(empty bool) []*netmap.Attribute {
|
||||
var res []*netmap.Attribute
|
||||
func GenerateAttributes(empty bool) []netmap.Attribute {
|
||||
var res []netmap.Attribute
|
||||
|
||||
if !empty {
|
||||
res = append(res,
|
||||
GenerateAttribute(false),
|
||||
GenerateAttribute(false),
|
||||
*GenerateAttribute(false),
|
||||
*GenerateAttribute(false),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ type Attribute struct {
|
|||
type NodeInfo struct {
|
||||
publicKey []byte
|
||||
addresses []string
|
||||
attributes []*Attribute
|
||||
attributes []Attribute
|
||||
state NodeState
|
||||
}
|
||||
|
||||
|
@ -444,7 +444,7 @@ func (ni *NodeInfo) IterateAddresses(f func(string) bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ni *NodeInfo) GetAttributes() []*Attribute {
|
||||
func (ni *NodeInfo) GetAttributes() []Attribute {
|
||||
if ni != nil {
|
||||
return ni.attributes
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ func (ni *NodeInfo) GetAttributes() []*Attribute {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ni *NodeInfo) SetAttributes(v []*Attribute) {
|
||||
func (ni *NodeInfo) SetAttributes(v []Attribute) {
|
||||
if ni != nil {
|
||||
ni.attributes = v
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue