[#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 {
|
if !presented {
|
||||||
var attr Attribute
|
index := len(attrs)
|
||||||
|
attrs = append(attrs, Attribute{})
|
||||||
attr.SetKey(key)
|
attrs[index].SetKey(key)
|
||||||
attr.SetValue(val)
|
attrs[index].SetValue(val)
|
||||||
|
|
||||||
attrs = append(attrs, &attr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,12 +206,10 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// zero subnet should be clearly removed with False value
|
// zero subnet should be clearly removed with False value
|
||||||
var attr Attribute
|
index := len(attrs)
|
||||||
|
attrs = append(attrs, Attribute{})
|
||||||
attr.SetKey(subnetAttributeKey(&id))
|
attrs[index].SetKey(subnetAttributeKey(&id))
|
||||||
attr.SetValue(attrSubnetValExit)
|
attrs[index].SetValue(attrSubnetValExit)
|
||||||
|
|
||||||
attrs = append(attrs, &attr)
|
|
||||||
} else {
|
} else {
|
||||||
entries++
|
entries++
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
"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/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
"github.com/stretchr/testify/require"
|
"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())
|
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) {
|
func TestWriteSubnetInfo(t *testing.T) {
|
||||||
t.Run("entry", func(t *testing.T) {
|
t.Run("entry", func(t *testing.T) {
|
||||||
t.Run("zero subnet", func(t *testing.T) {
|
t.Run("zero subnet", func(t *testing.T) {
|
||||||
|
@ -40,7 +92,7 @@ func TestWriteSubnetInfo(t *testing.T) {
|
||||||
attrs = node.GetAttributes()
|
attrs = node.GetAttributes()
|
||||||
require.Len(t, attrs, 1)
|
require.Len(t, attrs, 1)
|
||||||
|
|
||||||
attr := attrs[0]
|
attr := &attrs[0]
|
||||||
assertSubnetAttrKey(t, attr, 0)
|
assertSubnetAttrKey(t, attr, 0)
|
||||||
require.Equal(t, "False", attr.GetValue())
|
require.Equal(t, "False", attr.GetValue())
|
||||||
|
|
||||||
|
@ -76,7 +128,7 @@ func TestWriteSubnetInfo(t *testing.T) {
|
||||||
attrs := node.GetAttributes()
|
attrs := node.GetAttributes()
|
||||||
require.Len(t, attrs, 1)
|
require.Len(t, attrs, 1)
|
||||||
|
|
||||||
attr := attrs[0]
|
attr := &attrs[0]
|
||||||
assertSubnetAttrKey(t, attr, num)
|
assertSubnetAttrKey(t, attr, num)
|
||||||
require.Equal(t, "True", attr.GetValue())
|
require.Equal(t, "True", attr.GetValue())
|
||||||
|
|
||||||
|
@ -128,7 +180,7 @@ func TestSubnets(t *testing.T) {
|
||||||
attrExit.SetKey(subnetAttrKey(strconv.FormatUint(numExit, 10)))
|
attrExit.SetKey(subnetAttrKey(strconv.FormatUint(numExit, 10)))
|
||||||
attrExit.SetValue("False")
|
attrExit.SetValue("False")
|
||||||
|
|
||||||
attrs := []*netmap.Attribute{&attrEntry, &attrEntry}
|
attrs := []netmap.Attribute{attrEntry, attrEntry}
|
||||||
|
|
||||||
node.SetAttributes(attrs)
|
node.SetAttributes(attrs)
|
||||||
|
|
||||||
|
@ -157,7 +209,7 @@ func TestSubnets(t *testing.T) {
|
||||||
assertErr := func(attr netmap.Attribute) {
|
assertErr := func(attr netmap.Attribute) {
|
||||||
var node netmap.NodeInfo
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
node.SetAttributes([]*netmap.Attribute{&attr})
|
node.SetAttributes([]netmap.Attribute{attr})
|
||||||
|
|
||||||
require.Error(t, netmap.IterateSubnets(&node, func(refs.SubnetID) error {
|
require.Error(t, netmap.IterateSubnets(&node, func(refs.SubnetID) error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -200,7 +252,7 @@ func TestSubnets(t *testing.T) {
|
||||||
attr.SetKey(subnetAttrKey("321"))
|
attr.SetKey(subnetAttrKey("321"))
|
||||||
attr.SetValue("True")
|
attr.SetValue("True")
|
||||||
|
|
||||||
attrs := []*netmap.Attribute{&attr}
|
attrs := []netmap.Attribute{attr}
|
||||||
node.SetAttributes(attrs)
|
node.SetAttributes(attrs)
|
||||||
|
|
||||||
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
|
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
|
||||||
|
@ -237,7 +289,7 @@ func TestSubnets(t *testing.T) {
|
||||||
attr.SetKey(subnetAttrKey("99"))
|
attr.SetKey(subnetAttrKey("99"))
|
||||||
attr.SetValue("True")
|
attr.SetValue("True")
|
||||||
|
|
||||||
attrs := []*netmap.Attribute{&attr}
|
attrs := []netmap.Attribute{attr}
|
||||||
node.SetAttributes(attrs)
|
node.SetAttributes(attrs)
|
||||||
|
|
||||||
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
|
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) {
|
t.Run("all", func(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
node netmap.NodeInfo
|
node netmap.NodeInfo
|
||||||
attrs []*netmap.Attribute
|
attrs []netmap.Attribute
|
||||||
)
|
)
|
||||||
|
|
||||||
// enter to some non-zero subnet so that zero is not the only one
|
// 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.SetKey(subnetAttrKey(strconv.Itoa(i)))
|
||||||
attr.SetValue("True")
|
attr.SetValue("True")
|
||||||
|
|
||||||
attrs = append(attrs, &attr)
|
attrs = append(attrs, attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
node.SetAttributes(attrs)
|
node.SetAttributes(attrs)
|
||||||
|
@ -293,7 +345,7 @@ func TestSubnets(t *testing.T) {
|
||||||
attrOther.SetKey(subnetAttrKey("1"))
|
attrOther.SetKey(subnetAttrKey("1"))
|
||||||
attrOther.SetValue("True")
|
attrOther.SetValue("True")
|
||||||
|
|
||||||
node.SetAttributes([]*netmap.Attribute{&attrZero, &attrOther})
|
node.SetAttributes([]netmap.Attribute{attrZero, attrOther})
|
||||||
|
|
||||||
calledCount := 0
|
calledCount := 0
|
||||||
|
|
||||||
|
|
|
@ -297,7 +297,7 @@ func (a *Attribute) FromGRPCMessage(m grpc.Message) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AttributesToGRPC(as []*Attribute) (res []*netmap.NodeInfo_Attribute) {
|
func AttributesToGRPC(as []Attribute) (res []*netmap.NodeInfo_Attribute) {
|
||||||
if as != nil {
|
if as != nil {
|
||||||
res = make([]*netmap.NodeInfo_Attribute, 0, len(as))
|
res = make([]*netmap.NodeInfo_Attribute, 0, len(as))
|
||||||
|
|
||||||
|
@ -309,23 +309,17 @@ func AttributesToGRPC(as []*Attribute) (res []*netmap.NodeInfo_Attribute) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func AttributesFromGRPC(as []*netmap.NodeInfo_Attribute) (res []*Attribute, err error) {
|
func AttributesFromGRPC(as []*netmap.NodeInfo_Attribute) (res []Attribute, err error) {
|
||||||
if as != nil {
|
if as != nil {
|
||||||
res = make([]*Attribute, 0, len(as))
|
res = make([]Attribute, len(as))
|
||||||
|
|
||||||
for i := range as {
|
for i := range as {
|
||||||
var a *Attribute
|
|
||||||
|
|
||||||
if as[i] != nil {
|
if as[i] != nil {
|
||||||
a = new(Attribute)
|
err = res[i].FromGRPCMessage(as[i])
|
||||||
|
|
||||||
err = a.FromGRPCMessage(as[i])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, a)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -383,7 +383,7 @@ func (ni *NodeInfo) StableMarshal(buf []byte) ([]byte, error) {
|
||||||
offset += n
|
offset += n
|
||||||
|
|
||||||
for i := range ni.attributes {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,7 @@ func (ni *NodeInfo) StableSize() (size int) {
|
||||||
size += protoutil.RepeatedStringSize(addressNodeInfoField, ni.addresses)
|
size += protoutil.RepeatedStringSize(addressNodeInfoField, ni.addresses)
|
||||||
|
|
||||||
for i := range ni.attributes {
|
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))
|
size += protoutil.EnumSize(stateNodeInfoField, int32(ni.state))
|
||||||
|
|
|
@ -119,13 +119,13 @@ func GenerateAttribute(empty bool) *netmap.Attribute {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateAttributes(empty bool) []*netmap.Attribute {
|
func GenerateAttributes(empty bool) []netmap.Attribute {
|
||||||
var res []*netmap.Attribute
|
var res []netmap.Attribute
|
||||||
|
|
||||||
if !empty {
|
if !empty {
|
||||||
res = append(res,
|
res = append(res,
|
||||||
GenerateAttribute(false),
|
*GenerateAttribute(false),
|
||||||
GenerateAttribute(false),
|
*GenerateAttribute(false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ type Attribute struct {
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
publicKey []byte
|
publicKey []byte
|
||||||
addresses []string
|
addresses []string
|
||||||
attributes []*Attribute
|
attributes []Attribute
|
||||||
state NodeState
|
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 {
|
if ni != nil {
|
||||||
return ni.attributes
|
return ni.attributes
|
||||||
}
|
}
|
||||||
|
@ -452,7 +452,7 @@ func (ni *NodeInfo) GetAttributes() []*Attribute {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ni *NodeInfo) SetAttributes(v []*Attribute) {
|
func (ni *NodeInfo) SetAttributes(v []Attribute) {
|
||||||
if ni != nil {
|
if ni != nil {
|
||||||
ni.attributes = v
|
ni.attributes = v
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue