prysm-pulse/container/multi-value-slice/multi_value_slice_test.go
Radosław Kapka 44973b0bb3
Using the multi value slice for the beacon state (#12549)
* in progress...

* in progress...

* remove log

* log root

* Revert "Auxiliary commit to revert individual files from f12a609ea2a2f1e87e97321f3a717cd324b5ae97"

This reverts commit 5ae35edb6477d8d0ea4e94b273efc6590484da85.

* cleanup

* remove log

* remove whitespace

* remove logs

* more stuff

* copy

* always rebuild trie

* revert

* add state

* init state

* fix all

* uintptr

* move slice to new package

* lock in `Detach`

* remove constraint

* reorder

* blockroots and stateroots

* fill roots in empty()

* fix hasher

* implement slice for balances and inactivity scores

* detach in setters

* Revert "implement slice for balances and inactivity scores"

This reverts commit 59eb9df8d766cb1c44a7eb5b3f5e3c042249943d.

# Conflicts:
#	beacon-chain/state/state-native/setters_validator.go

* use counter to track states

* typos

* rename interface

* balances

* gauge

* some improvements

* first try with map

* fix

* inactivity scores in progress

* fix test

# Conflicts:
#	beacon-chain/state/state-native/helpers_test.go

* test fixes

* ToProto fix

* copy roots

* validators

* build fixes

* fix bug in `ToProto`

* fix fuzz test

* fix bug in slice getters

* fix state equality checks

* make tests pass

* make tests pass

* more test updates

* Revert "Auxiliary commit to revert individual files from 34e7344bff08a589e6341bb1829e3cb74159e878"

This reverts commit ecd64efa8917f37ca41460e0356ff007fe55dd9d.

* Revert "make tests pass"

This reverts commit 0cf00f19eecf4678cd2b866dd107f3179d0426ef.

* Revert "make tests pass"

This reverts commit 521b65e1d2e13be3d720f333008b6838a8e78878.

* pass tests

* deepequal identifiable types

* Deflake `cloners_test.go`

* feature flag for block roots

* feature flag

* remove recursive locks

* reduce complexity of rootSelector

* fix randao mixes root

* some fixes

* revisit tests

* revert change to field trie helpers

* initialize field map for tests

* remove whitespace

* initialize roots with proper length

* more fixes

* out of bounds message fix

* optimize length calculation

* remove call to Len in PubkeyAtIndex

* don't log deposits

* unit tests

* unit tests

* fix

* comments

* test fixes

* id

* remove Enumerator interface

* review feedback

* simplify field trie

* bring back fieldtrie package

* fix bazel file

* use handle32ByteArrays for root computation

* fix locks

* metrics

* bzl

* simplify some things

* use htr in state test

* remove code from require package

* gzl

* more htr

* Fuzzing of the multi-value slice

* assert values

* getter optimizations

* use At when reading from validators

* Nishant's review

* restore safe copy

* remove empty line

* build fix

* restore how we get root at index for deafult mode

* more review comments

* optimize default behavior

* simplify Slice calls

* test fix

* remove unnecessary package

* remove unused setter

* make fieldMap unexported

* some improvements in state package

* call `Slice` instead of manually copying

* unlock in ReadFromEveryValidator

* Potuz's comments

* lock the state when reading from all validators

# Conflicts:
#	beacon-chain/state/state-native/getters_validator.go

* add back preston's changes

* add index

---------

Co-authored-by: Potuz <potuz@prysmaticlabs.com>
Co-authored-by: nisdas <nishdas93@gmail.com>
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2023-10-06 01:10:05 +00:00

643 lines
19 KiB
Go

package mvslice
import (
"math/rand"
"testing"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
type testObject struct {
id uint64
}
func (o *testObject) Id() uint64 {
return o.id
}
func (o *testObject) SetId(id uint64) {
o.id = id
}
func TestLen(t *testing.T) {
s := &Slice[int, *testObject]{}
s.Init([]int{1, 2, 3})
s.cachedLengths[1] = 123
t.Run("cached", func(t *testing.T) {
assert.Equal(t, 123, s.Len(&testObject{id: 1}))
})
t.Run("not cached", func(t *testing.T) {
assert.Equal(t, 3, s.Len(&testObject{id: 999}))
})
}
func TestCopy(t *testing.T) {
// What we want to check:
// - shared value is copied
// - when the source object has an individual value, it is copied
// - when the source object does not have an individual value, the shared value is copied
// - when the source object has an appended value, it is copied
// - when the source object does not have an appended value, nothing is copied
// - length of destination object is cached
s := setup()
src := &testObject{id: 1}
dst := &testObject{id: 999}
s.Copy(src, dst)
assert.Equal(t, (*MultiValueItem[int])(nil), s.individualItems[0])
assertIndividualFound(t, s, dst.id, 1, 1)
assertIndividualFound(t, s, dst.id, 2, 3)
assertIndividualFound(t, s, dst.id, 3, 1)
assertIndividualNotFound(t, s, dst.id, 4)
assertAppendedFound(t, s, dst.id, 0, 1)
assertAppendedFound(t, s, dst.id, 1, 3)
assertAppendedNotFound(t, s, dst.id, 2)
l, ok := s.cachedLengths[999]
require.Equal(t, true, ok)
assert.Equal(t, 7, l)
}
func TestValue(t *testing.T) {
// What we want to check:
// - correct values are returned for first object
// - correct values are returned for second object
// - correct values are returned for an object without appended items
s := setup()
first := &testObject{id: 1}
second := &testObject{id: 2}
v := s.Value(first)
require.Equal(t, 7, len(v))
assert.Equal(t, 123, v[0])
assert.Equal(t, 1, v[1])
assert.Equal(t, 3, v[2])
assert.Equal(t, 1, v[3])
assert.Equal(t, 123, v[4])
assert.Equal(t, 1, v[5])
assert.Equal(t, 3, v[6])
v = s.Value(second)
require.Equal(t, 8, len(v))
assert.Equal(t, 123, v[0])
assert.Equal(t, 2, v[1])
assert.Equal(t, 3, v[2])
assert.Equal(t, 123, v[3])
assert.Equal(t, 2, v[4])
assert.Equal(t, 2, v[5])
assert.Equal(t, 3, v[6])
assert.Equal(t, 2, v[7])
s = &Slice[int, *testObject]{}
s.Init([]int{1, 2, 3})
v = s.Value(&testObject{id: 999})
require.Equal(t, 3, len(v))
assert.Equal(t, 1, v[0])
assert.Equal(t, 2, v[1])
assert.Equal(t, 3, v[2])
}
func TestAt(t *testing.T) {
// What we want to check:
// - correct values are returned for first object
// - correct values are returned for second object
// - ERROR when index too large in general
// - ERROR when index not too large in general, but too large for an object
s := setup()
first := &testObject{id: 1}
second := &testObject{id: 2}
v, err := s.At(first, 0)
require.NoError(t, err)
assert.Equal(t, 123, v)
v, err = s.At(first, 1)
require.NoError(t, err)
assert.Equal(t, 1, v)
v, err = s.At(first, 2)
require.NoError(t, err)
assert.Equal(t, 3, v)
v, err = s.At(first, 3)
require.NoError(t, err)
assert.Equal(t, 1, v)
v, err = s.At(first, 4)
require.NoError(t, err)
assert.Equal(t, 123, v)
v, err = s.At(first, 5)
require.NoError(t, err)
assert.Equal(t, 1, v)
v, err = s.At(first, 6)
require.NoError(t, err)
assert.Equal(t, 3, v)
_, err = s.At(first, 7)
assert.ErrorContains(t, "index 7 out of bounds", err)
v, err = s.At(second, 0)
require.NoError(t, err)
assert.Equal(t, 123, v)
v, err = s.At(second, 1)
require.NoError(t, err)
assert.Equal(t, 2, v)
v, err = s.At(second, 2)
require.NoError(t, err)
assert.Equal(t, 3, v)
v, err = s.At(second, 3)
require.NoError(t, err)
assert.Equal(t, 123, v)
v, err = s.At(second, 4)
require.NoError(t, err)
assert.Equal(t, 2, v)
v, err = s.At(second, 5)
require.NoError(t, err)
assert.Equal(t, 2, v)
v, err = s.At(second, 6)
require.NoError(t, err)
assert.Equal(t, 3, v)
v, err = s.At(second, 7)
require.NoError(t, err)
assert.Equal(t, 2, v)
_, err = s.At(second, 8)
assert.ErrorContains(t, "index 8 out of bounds", err)
}
func TestUpdateAt(t *testing.T) {
// What we want to check:
// - shared value is updated only for the updated object, creating a new individual value (shared value remains the same)
// - individual value (different for both objects) is updated to a third value
// - individual value (different for both objects) is updated to the other object's value
// - individual value (equal for both objects) is updated
// - individual value existing only for the updated object is updated
// - individual value existing only for the other-object appends an item to the individual value
// - individual value updated to the original shared value removes that individual value
// - appended value (different for both objects) is updated to a third value
// - appended value (different for both objects) is updated to the other object's value
// - appended value (equal for both objects) is updated
// - appended value existing for one object is updated
// - ERROR when index too large in general
// - ERROR when index not too large in general, but too large for an object
s := setup()
first := &testObject{id: 1}
second := &testObject{id: 2}
require.NoError(t, s.UpdateAt(first, 0, 999))
assert.Equal(t, 123, s.sharedItems[0])
assertIndividualFound(t, s, first.id, 0, 999)
assertIndividualNotFound(t, s, second.id, 0)
require.NoError(t, s.UpdateAt(first, 1, 999))
assertIndividualFound(t, s, first.id, 1, 999)
assertIndividualFound(t, s, second.id, 1, 2)
require.NoError(t, s.UpdateAt(first, 1, 2))
assertIndividualFound(t, s, first.id, 1, 2)
assertIndividualFound(t, s, second.id, 1, 2)
require.NoError(t, s.UpdateAt(first, 2, 999))
assertIndividualFound(t, s, first.id, 2, 999)
assertIndividualFound(t, s, second.id, 2, 3)
require.NoError(t, s.UpdateAt(first, 3, 999))
assertIndividualFound(t, s, first.id, 3, 999)
assertIndividualNotFound(t, s, second.id, 3)
require.NoError(t, s.UpdateAt(first, 4, 999))
assertIndividualFound(t, s, first.id, 4, 999)
assertIndividualFound(t, s, second.id, 4, 2)
require.NoError(t, s.UpdateAt(first, 4, 123))
assertIndividualNotFound(t, s, first.id, 4)
assertIndividualFound(t, s, second.id, 4, 2)
require.NoError(t, s.UpdateAt(first, 5, 999))
assertAppendedFound(t, s, first.id, 0, 999)
assertAppendedFound(t, s, second.id, 0, 2)
require.NoError(t, s.UpdateAt(first, 5, 2))
assertAppendedFound(t, s, first.id, 0, 2)
assertAppendedFound(t, s, second.id, 0, 2)
require.NoError(t, s.UpdateAt(first, 6, 999))
assertAppendedFound(t, s, first.id, 1, 999)
assertAppendedFound(t, s, second.id, 1, 3)
// we update the second object because there are no more appended items for the first object
require.NoError(t, s.UpdateAt(second, 7, 999))
assertAppendedNotFound(t, s, first.id, 2)
assertAppendedFound(t, s, second.id, 2, 999)
assert.ErrorContains(t, "index 7 out of bounds", s.UpdateAt(first, 7, 999))
assert.ErrorContains(t, "index 8 out of bounds", s.UpdateAt(second, 8, 999))
}
func TestAppend(t *testing.T) {
// What we want to check:
// - appending first item ever to the slice
// - appending an item to an object when there is no corresponding item for the other object
// - appending an item to an object when there is a corresponding item with same value for the other object
// - appending an item to an object when there is a corresponding item with different value for the other object
// - we also want to check that cached length is properly updated after every append
// we want to start with the simplest slice possible
s := &Slice[int, *testObject]{}
s.Init([]int{0})
first := &testObject{id: 1}
second := &testObject{id: 2}
// append first value ever
s.Append(first, 1)
require.Equal(t, 1, len(s.appendedItems))
assertAppendedFound(t, s, first.id, 0, 1)
assertAppendedNotFound(t, s, second.id, 0)
l, ok := s.cachedLengths[first.id]
require.Equal(t, true, ok)
assert.Equal(t, 2, l)
_, ok = s.cachedLengths[second.id]
assert.Equal(t, false, ok)
// append one more value to the first object, so that we can test two append scenarios for the second object
s.Append(first, 1)
// append the first value to the second object, equal to the value for the first object
s.Append(second, 1)
require.Equal(t, 2, len(s.appendedItems))
assertAppendedFound(t, s, first.id, 0, 1)
assertAppendedFound(t, s, second.id, 0, 1)
l, ok = s.cachedLengths[first.id]
require.Equal(t, true, ok)
assert.Equal(t, 3, l)
l, ok = s.cachedLengths[second.id]
assert.Equal(t, true, ok)
assert.Equal(t, 2, l)
// append the first value to the second object, different than the value for the first object
s.Append(second, 2)
require.Equal(t, 2, len(s.appendedItems))
assertAppendedFound(t, s, first.id, 1, 1)
assertAppendedFound(t, s, second.id, 1, 2)
l, ok = s.cachedLengths[first.id]
require.Equal(t, true, ok)
assert.Equal(t, 3, l)
l, ok = s.cachedLengths[second.id]
assert.Equal(t, true, ok)
assert.Equal(t, 3, l)
}
func TestDetach(t *testing.T) {
// What we want to check:
// - no individual or appended items left after detaching an object
// - length removed from cache
s := setup()
obj := &testObject{id: 1}
s.Detach(obj)
for _, item := range s.individualItems {
found := false
for _, v := range item.Values {
for _, o := range v.ids {
if o == obj.id {
found = true
}
}
}
assert.Equal(t, false, found)
}
for _, item := range s.appendedItems {
found := false
for _, v := range item.Values {
for _, o := range v.ids {
if o == obj.id {
found = true
}
}
}
assert.Equal(t, false, found)
}
_, ok := s.cachedLengths[obj.id]
assert.Equal(t, false, ok)
}
// Share the slice between 2 objects.
// Index 0: Shared value
// Index 1: Different individual value
// Index 2: Same individual value
// Index 3: Individual value ONLY for the first object
// Index 4: Individual value ONLY for the second object
// Index 5: Different appended value
// Index 6: Same appended value
// Index 7: Appended value ONLY for the second object
func setup() *Slice[int, *testObject] {
s := &Slice[int, *testObject]{}
s.Init([]int{123, 123, 123, 123, 123})
s.individualItems[1] = &MultiValueItem[int]{
Values: []*Value[int]{
{
val: 1,
ids: []uint64{1},
},
{
val: 2,
ids: []uint64{2},
},
},
}
s.individualItems[2] = &MultiValueItem[int]{
Values: []*Value[int]{
{
val: 3,
ids: []uint64{1, 2},
},
},
}
s.individualItems[3] = &MultiValueItem[int]{
Values: []*Value[int]{
{
val: 1,
ids: []uint64{1},
},
},
}
s.individualItems[4] = &MultiValueItem[int]{
Values: []*Value[int]{
{
val: 2,
ids: []uint64{2},
},
},
}
s.appendedItems = []*MultiValueItem[int]{
{
Values: []*Value[int]{
{
val: 1,
ids: []uint64{1},
},
{
val: 2,
ids: []uint64{2},
},
},
},
{
Values: []*Value[int]{
{
val: 3,
ids: []uint64{1, 2},
},
},
},
{
Values: []*Value[int]{
{
val: 2,
ids: []uint64{2},
},
},
},
}
s.cachedLengths[1] = 7
s.cachedLengths[2] = 8
return s
}
func assertIndividualFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64, expected int) {
found := false
for _, v := range slice.individualItems[itemIndex].Values {
for _, o := range v.ids {
if o == id {
found = true
assert.Equal(t, expected, v.val)
}
}
}
assert.Equal(t, true, found)
}
func assertIndividualNotFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64) {
found := false
for _, v := range slice.individualItems[itemIndex].Values {
for _, o := range v.ids {
if o == id {
found = true
}
}
}
assert.Equal(t, false, found)
}
func assertAppendedFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64, expected int) {
found := false
for _, v := range slice.appendedItems[itemIndex].Values {
for _, o := range v.ids {
if o == id {
found = true
assert.Equal(t, expected, v.val)
}
}
}
assert.Equal(t, true, found)
}
func assertAppendedNotFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64) {
found := false
for _, v := range slice.appendedItems[itemIndex].Values {
for _, o := range v.ids {
if o == id {
found = true
}
}
}
assert.Equal(t, false, found)
}
func BenchmarkValue(b *testing.B) {
const _100k = 100000
const _1m = 1000000
const _10m = 10000000
b.Run("100,000 shared items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _100k))
for i := 0; i < b.N; i++ {
s.Value(&testObject{})
}
})
b.Run("100,000 equal individual items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _100k))
s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}
objs := make([]*testObject, _100k)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, uint64(i))
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_100k)])
}
})
b.Run("100,000 different individual items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _100k))
objs := make([]*testObject, _100k)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}}
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_100k)])
}
})
b.Run("100,000 shared items and 100,000 equal appended items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _100k))
s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}}
objs := make([]*testObject, _100k)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, uint64(i))
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_100k)])
}
})
b.Run("100,000 shared items and 100,000 different appended items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _100k))
s.appendedItems = []*MultiValueItem[int]{}
objs := make([]*testObject, _100k)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}})
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_100k)])
}
})
b.Run("1,000,000 shared items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _1m))
for i := 0; i < b.N; i++ {
s.Value(&testObject{})
}
})
b.Run("1,000,000 equal individual items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _1m))
s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}
objs := make([]*testObject, _1m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, uint64(i))
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_1m)])
}
})
b.Run("1,000,000 different individual items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _1m))
objs := make([]*testObject, _1m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}}
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_1m)])
}
})
b.Run("1,000,000 shared items and 1,000,000 equal appended items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _1m))
s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}}
objs := make([]*testObject, _1m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, uint64(i))
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_1m)])
}
})
b.Run("1,000,000 shared items and 1,000,000 different appended items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _1m))
s.appendedItems = []*MultiValueItem[int]{}
objs := make([]*testObject, _1m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}})
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_1m)])
}
})
b.Run("10,000,000 shared items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _10m))
for i := 0; i < b.N; i++ {
s.Value(&testObject{})
}
})
b.Run("10,000,000 equal individual items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _10m))
s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}
objs := make([]*testObject, _10m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, uint64(i))
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_10m)])
}
})
b.Run("10,000,000 different individual items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _10m))
objs := make([]*testObject, _10m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}}
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_10m)])
}
})
b.Run("10,000,000 shared items and 10,000,000 equal appended items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _10m))
s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}}
objs := make([]*testObject, _10m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, uint64(i))
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_10m)])
}
})
b.Run("10,000,000 shared items and 10,000,000 different appended items", func(b *testing.B) {
s := &Slice[int, *testObject]{}
s.Init(make([]int, _10m))
s.appendedItems = []*MultiValueItem[int]{}
objs := make([]*testObject, _10m)
for i := 0; i < len(objs); i++ {
objs[i] = &testObject{id: uint64(i)}
s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}})
}
for i := 0; i < b.N; i++ {
s.Value(objs[rand.Intn(_10m)])
}
})
}