Skip to content

Commit

Permalink
Compute max map value size using map key size
Browse files Browse the repository at this point in the history
Previously, both max map key size and max map value size were the same
(about half of max map element size).  However, key size can be much
smaller than max limit and max value size doesn't benefit from smaller key.

Improve this by computing max map value size to subtract encoded key size
from max map element size.  So large value can be stored along with
small key to reduce number of slabs.

While at it, also replace exported settings with exported functions.
  • Loading branch information
fxamacker committed Jun 26, 2023
1 parent bc0184e commit 3c855fb
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 60 deletions.
6 changes: 3 additions & 3 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ func (a *ArrayDataSlab) Set(storage SlabStorage, address Address, index uint64,
oldElem := a.elements[index]
oldSize := oldElem.ByteSize()

storable, err := value.Storable(storage, address, MaxInlineArrayElementSize)
storable, err := value.Storable(storage, address, maxInlineArrayElementSize)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand All @@ -503,7 +503,7 @@ func (a *ArrayDataSlab) Insert(storage SlabStorage, address Address, index uint6
return NewIndexOutOfBoundsError(index, 0, uint64(len(a.elements)))
}

storable, err := value.Storable(storage, address, MaxInlineArrayElementSize)
storable, err := value.Storable(storage, address, maxInlineArrayElementSize)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand Down Expand Up @@ -2545,7 +2545,7 @@ func NewArrayFromBatchData(storage SlabStorage, address Address, typeInfo TypeIn

}

storable, err := value.Storable(storage, address, MaxInlineArrayElementSize)
storable, err := value.Storable(storage, address, maxInlineArrayElementSize)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand Down
10 changes: 5 additions & 5 deletions array_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func benchmarkArray(b *testing.B, initialArraySize, numberOfElements int) {
// setup
for i := 0; i < initialArraySize; i++ {
v := RandomValue(r)
storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)
totalRawDataSize += storable.ByteSize()
err = array.Append(v)
Expand All @@ -110,7 +110,7 @@ func benchmarkArray(b *testing.B, initialArraySize, numberOfElements int) {
for i := 0; i < numberOfElements; i++ {
v := RandomValue(r)

storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)

totalRawDataSize += storable.ByteSize()
Expand Down Expand Up @@ -148,7 +148,7 @@ func benchmarkArray(b *testing.B, initialArraySize, numberOfElements int) {
ind := r.Intn(int(array.Count()))
v := RandomValue(r)

storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)

totalRawDataSize += storable.ByteSize()
Expand Down Expand Up @@ -221,7 +221,7 @@ func benchmarkLongTermImpactOnMemory(b *testing.B, initialArraySize, numberOfOps
for i := 0; i < initialArraySize; i++ {
v := RandomValue(r)

storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)

totalRawDataSize += storable.ByteSize()
Expand All @@ -243,7 +243,7 @@ func benchmarkLongTermImpactOnMemory(b *testing.B, initialArraySize, numberOfOps
case 1: // insert
v := RandomValue(r)

storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)

totalRawDataSize += storable.ByteSize()
Expand Down
4 changes: 2 additions & 2 deletions array_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,9 @@ func validArraySlab(
for _, e := range dataSlab.elements {

// Verify element size is <= inline size
if e.ByteSize() > uint32(MaxInlineArrayElementSize) {
if e.ByteSize() > uint32(maxInlineArrayElementSize) {
return 0, nil, nil, NewFatalError(fmt.Errorf("data slab %d element %s size %d is too large, want < %d",
id, e, e.ByteSize(), MaxInlineArrayElementSize))
id, e, e.ByteSize(), maxInlineArrayElementSize))
}

computedSize += e.ByteSize()
Expand Down
36 changes: 18 additions & 18 deletions array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ func TestArraySetRandomValues(t *testing.T) {

for i := uint64(0); i < arraySize; i++ {
oldValue := values[i]
newValue := randomValue(r, int(MaxInlineArrayElementSize))
newValue := randomValue(r, int(maxInlineArrayElementSize))
values[i] = newValue

existingStorable, err := array.Set(i, newValue)
Expand Down Expand Up @@ -1129,7 +1129,7 @@ func TestArrayInsertRandomValues(t *testing.T) {

values := make([]Value, arraySize)
for i := uint64(0); i < arraySize; i++ {
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))
values[arraySize-i-1] = v

err := array.Insert(0, v)
Expand All @@ -1154,7 +1154,7 @@ func TestArrayInsertRandomValues(t *testing.T) {

values := make([]Value, arraySize)
for i := uint64(0); i < arraySize; i++ {
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))
values[i] = v

err := array.Insert(i, v)
Expand All @@ -1180,7 +1180,7 @@ func TestArrayInsertRandomValues(t *testing.T) {
values := make([]Value, arraySize)
for i := uint64(0); i < arraySize; i++ {
k := r.Intn(int(i) + 1)
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))

copy(values[k+1:], values[k:])
values[k] = v
Expand Down Expand Up @@ -1212,7 +1212,7 @@ func TestArrayRemoveRandomValues(t *testing.T) {
values := make([]Value, arraySize)
// Insert n random values into array
for i := uint64(0); i < arraySize; i++ {
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))
values[i] = v

err := array.Insert(i, v)
Expand Down Expand Up @@ -1279,15 +1279,15 @@ func testArrayAppendSetInsertRemoveRandomValues(
switch nextOp {

case ArrayAppendOp:
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))
values = append(values, v)

err := array.Append(v)
require.NoError(t, err)

case ArraySetOp:
k := r.Intn(int(array.Count()))
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))

oldV := values[k]

Expand All @@ -1307,7 +1307,7 @@ func testArrayAppendSetInsertRemoveRandomValues(

case ArrayInsertOp:
k := r.Intn(int(array.Count() + 1))
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))

if k == int(array.Count()) {
values = append(values, v)
Expand Down Expand Up @@ -1879,7 +1879,7 @@ func TestArrayStringElement(t *testing.T) {

r := newRand(t)

stringSize := int(MaxInlineArrayElementSize - 3)
stringSize := int(maxInlineArrayElementSize - 3)

values := make([]Value, arraySize)
for i := uint64(0); i < arraySize; i++ {
Expand Down Expand Up @@ -1912,7 +1912,7 @@ func TestArrayStringElement(t *testing.T) {

r := newRand(t)

stringSize := int(MaxInlineArrayElementSize + 512)
stringSize := int(maxInlineArrayElementSize + 512)

values := make([]Value, arraySize)
for i := uint64(0); i < arraySize; i++ {
Expand Down Expand Up @@ -2209,7 +2209,7 @@ func TestArrayFromBatchData(t *testing.T) {
var values []Value
var v Value

v = NewStringValue(strings.Repeat("a", int(MaxInlineArrayElementSize-2)))
v = NewStringValue(strings.Repeat("a", int(maxInlineArrayElementSize-2)))
values = append(values, v)

err = array.Insert(0, v)
Expand Down Expand Up @@ -2265,7 +2265,7 @@ func TestArrayFromBatchData(t *testing.T) {
require.NoError(t, err)
}

v = NewStringValue(strings.Repeat("a", int(MaxInlineArrayElementSize-2)))
v = NewStringValue(strings.Repeat("a", int(maxInlineArrayElementSize-2)))
values = append(values, nil)
copy(values[25+1:], values[25:])
values[25] = v
Expand Down Expand Up @@ -2312,7 +2312,7 @@ func TestArrayFromBatchData(t *testing.T) {

values := make([]Value, arraySize)
for i := uint64(0); i < arraySize; i++ {
v := randomValue(r, int(MaxInlineArrayElementSize))
v := randomValue(r, int(maxInlineArrayElementSize))
values[i] = v

err := array.Append(v)
Expand Down Expand Up @@ -2361,17 +2361,17 @@ func TestArrayFromBatchData(t *testing.T) {
var values []Value
var v Value

v = NewStringValue(randStr(r, int(MaxInlineArrayElementSize-2)))
v = NewStringValue(randStr(r, int(maxInlineArrayElementSize-2)))
values = append(values, v)
err = array.Append(v)
require.NoError(t, err)

v = NewStringValue(randStr(r, int(MaxInlineArrayElementSize-2)))
v = NewStringValue(randStr(r, int(maxInlineArrayElementSize-2)))
values = append(values, v)
err = array.Append(v)
require.NoError(t, err)

v = NewStringValue(randStr(r, int(MaxInlineArrayElementSize-2)))
v = NewStringValue(randStr(r, int(maxInlineArrayElementSize-2)))
values = append(values, v)
err = array.Append(v)
require.NoError(t, err)
Expand Down Expand Up @@ -2438,7 +2438,7 @@ func TestArrayMaxInlineElement(t *testing.T) {
var values []Value
for i := 0; i < 2; i++ {
// String length is MaxInlineArrayElementSize - 3 to account for string encoding overhead.
v := NewStringValue(randStr(r, int(MaxInlineArrayElementSize-3)))
v := NewStringValue(randStr(r, int(maxInlineArrayElementSize-3)))
values = append(values, v)

err = array.Append(v)
Expand Down Expand Up @@ -2561,7 +2561,7 @@ func TestArraySlabDump(t *testing.T) {
array, err := NewArray(storage, address, typeInfo)
require.NoError(t, err)

err = array.Append(NewStringValue(strings.Repeat("a", int(MaxInlineArrayElementSize))))
err = array.Append(NewStringValue(strings.Repeat("a", int(maxInlineArrayElementSize))))
require.NoError(t, err)

want := []string{
Expand Down
4 changes: 2 additions & 2 deletions basicarray.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func (a *BasicArray) Get(index uint64) (Value, error) {
}

func (a *BasicArray) Set(index uint64, v Value) error {
storable, err := v.Storable(a.storage, a.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(a.storage, a.Address(), maxInlineArrayElementSize)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand All @@ -332,7 +332,7 @@ func (a *BasicArray) Append(v Value) error {
}

func (a *BasicArray) Insert(index uint64, v Value) error {
storable, err := v.Storable(a.storage, a.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(a.storage, a.Address(), maxInlineArrayElementSize)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand Down
8 changes: 4 additions & 4 deletions basicarray_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func benchmarkBasicArray(b *testing.B, initialArraySize, numberOfElements int) {
// setup
for i := 0; i < initialArraySize; i++ {
v := RandomValue(r)
storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)
totalRawDataSize += storable.ByteSize()
err = array.Append(v)
Expand All @@ -98,7 +98,7 @@ func benchmarkBasicArray(b *testing.B, initialArraySize, numberOfElements int) {
start = time.Now()
for i := 0; i < numberOfElements; i++ {
v := RandomValue(r)
storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)
totalRawDataSize += storable.ByteSize()
err = array.Append(v)
Expand All @@ -117,7 +117,7 @@ func benchmarkBasicArray(b *testing.B, initialArraySize, numberOfElements int) {
ind := r.Intn(int(array.Count()))
s, err := array.Remove(uint64(ind))
require.NoError(b, err)
storable, err := s.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := s.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)
totalRawDataSize -= storable.ByteSize()
}
Expand All @@ -133,7 +133,7 @@ func benchmarkBasicArray(b *testing.B, initialArraySize, numberOfElements int) {
for i := 0; i < numberOfElements; i++ {
ind := r.Intn(int(array.Count()))
v := RandomValue(r)
storable, err := v.Storable(storage, array.Address(), MaxInlineArrayElementSize)
storable, err := v.Storable(storage, array.Address(), maxInlineArrayElementSize)
require.NoError(b, err)
totalRawDataSize += storable.ByteSize()
err = array.Insert(uint64(ind), v)
Expand Down
8 changes: 4 additions & 4 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,13 +453,13 @@ func newElementFromData(cborDec *cbor.StreamDecoder, decodeStorable StorableDeco

func newSingleElement(storage SlabStorage, address Address, key Value, value Value) (*singleElement, error) {

ks, err := key.Storable(storage, address, MaxInlineMapKeyOrValueSize)
ks, err := key.Storable(storage, address, maxInlineMapKeySize)
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get key's storable")
}

vs, err := value.Storable(storage, address, MaxInlineMapKeyOrValueSize)
vs, err := value.Storable(storage, address, maxInlineMapValueSize(uint64(ks.ByteSize())))
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand Down Expand Up @@ -598,7 +598,7 @@ func (e *singleElement) Set(
if equal {
existingValue := e.value

valueStorable, err := value.Storable(storage, address, MaxInlineMapKeyOrValueSize)
valueStorable, err := value.Storable(storage, address, maxInlineMapValueSize(uint64(e.key.ByteSize())))
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return nil, nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand Down Expand Up @@ -1925,7 +1925,7 @@ func (e *singleElements) Set(storage SlabStorage, address Address, b DigesterBui

oldSize := elem.Size()

vs, err := value.Storable(storage, address, MaxInlineMapKeyOrValueSize)
vs, err := value.Storable(storage, address, maxInlineMapValueSize(uint64(elem.key.ByteSize())))
if err != nil {
// Wrap err as external error (if needed) because err is returned by Value interface.
return nil, wrapErrorfAsExternalErrorIfNeeded(err, "failed to get value's storable")
Expand Down
Loading

0 comments on commit 3c855fb

Please sign in to comment.