Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions gno.land/pkg/integration/testdata/gc.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Init
## deploy realm
loadpkg gno.land/r/gc $WORK/r/gc

## start a new node
gnoland start



gnokey maketx call -pkgpath gno.land/r/gc -func Alloc -gas-fee 100000ugnot -gas-wanted 3000000 -simulate skip -broadcast -chainid tendermint_test test1
stdout 'GAS USED: 576890'


-- r/gc/gc.gno --
package gc

func gen() {
_ = make([]byte, 250*1024*1024)
}

func Alloc() {
for i := 0; i < 100; i++ {
gen()
gen()
}
}
143 changes: 130 additions & 13 deletions gnovm/pkg/gnolang/alloc.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package gnolang

import (
"fmt"
)

// Keeps track of in-memory allocations.
// In the future, allocations within realm boundaries will be
// (optionally?) condensed (objects to be GC'd will be discarded),
// but for now, allocations strictly increment across the whole tx.
type Allocator struct {
maxBytes int64
bytes int64
collect func() (left int64, ok bool) // gc callback
}

// for gonative, which doesn't consider the allocator.
Expand All @@ -22,16 +27,20 @@
_allocStructValue = 152
_allocArrayValue = 176
_allocSliceValue = 40
_allocFuncValue = 136
_allocFuncValue = 312
_allocMapValue = 144
_allocBoundMethodValue = 176
_allocBlock = 464
_allocBlock = 472
_allocPackageValue = 240
_allocTypeValue = 16
_allocTypedValue = 40
_allocRefValue = 72
_allocBigint = 200 // XXX
_allocBigdec = 200 // XXX
_allocType = 200 // XXX
_allocAny = 200 // XXX
_allocValue = 16 // interface
_allocName = 16 // string
)

const (
Expand All @@ -53,12 +62,12 @@
allocBoundMethod = _allocBase + _allocPointer + _allocBoundMethodValue
allocBlock = _allocBase + _allocPointer + _allocBlock
allocBlockItem = _allocTypedValue
allocRefValue = _allocBase + +_allocRefValue
allocType = _allocBase + _allocPointer + _allocType
// allocDataByte = 1
// allocPackge = 1
allocAmino = _allocBase + _allocPointer + _allocAny
allocAminoByte = 10 // XXX
allocHeapItem = _allocBase + _allocPointer + _allocTypedValue
allocDataByte = 1
allocPackage = _allocBase + _allocPointer + _allocPackageValue
allocHeapItem = _allocBase + _allocPointer + _allocTypedValue
allocTypedValue = _allocTypedValue
)

func NewAllocator(maxBytes int64) *Allocator {
Expand All @@ -70,6 +79,18 @@
}
}

func (alloc *Allocator) SetGCFn(f func() (int64, bool)) {
alloc.collect = f
}

func (alloc *Allocator) MemStats() string {
if alloc == nil {
return "nil allocator"

Check warning on line 88 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L88

Added line #L88 was not covered by tests
} else {
return fmt.Sprintf("Allocator{maxBytes:%d, bytes:%d}", alloc.maxBytes, alloc.bytes)
}
}

func (alloc *Allocator) Status() (maxBytes int64, bytes int64) {
return alloc.maxBytes, alloc.bytes
}
Expand Down Expand Up @@ -100,7 +121,17 @@

alloc.bytes += size
if alloc.bytes > alloc.maxBytes {
panic("allocation limit exceeded")
if left, ok := alloc.collect(); !ok {
panic("should not happen, allocation limit exceeded while gc.")

Check warning on line 125 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L125

Added line #L125 was not covered by tests
} else { // retry
if debug {
debug.Printf("%d left after GC, required size: %d\n", left, size)
}

Check warning on line 129 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L128-L129

Added lines #L128 - L129 were not covered by tests
alloc.bytes += size
if alloc.bytes > alloc.maxBytes {
panic("allocation limit exceeded")
}
}
}
}

Expand Down Expand Up @@ -167,11 +198,6 @@
alloc.Allocate(allocType)
}

// NOTE: a reasonable max-bounds calculation for simplicity.
func (alloc *Allocator) AllocateAmino(l int64) {
alloc.Allocate(allocAmino + allocAminoByte*l)
}

func (alloc *Allocator) AllocateHeapItem() {
alloc.Allocate(allocHeapItem)
}
Expand Down Expand Up @@ -314,3 +340,94 @@
alloc.AllocateHeapItem()
return &HeapItemValue{Value: tv}
}

// -----------------------------------------------

func (pv *PackageValue) GetShallowSize() int64 {
var (
allocFNames int64
allocFBlocks int64
allocFBlocksMap int64
)

for _, name := range pv.FNames {
allocFNames += int64(len(name)) + _allocName // string is counted as shallow size.
}

allocFBlocks = int64(len(pv.FBlocks)) * _allocValue

for name := range pv.fBlocksMap {
allocFBlocksMap += int64(len(name)) + _allocName // key
allocFBlocksMap += _allocPointer // *Block
}

return allocPackage + allocFNames + allocFBlocks + allocFBlocksMap
}

func (b *Block) GetShallowSize() int64 {
return allocBlock + allocBlockItem*int64(len(b.Values))
}

func (av *ArrayValue) GetShallowSize() int64 {
if av.Data != nil {
return allocArray + int64(len(av.Data))
} else {
return allocArray + int64(len(av.List)*allocArrayItem)
}
}

func (sv *StructValue) GetShallowSize() int64 {
return allocStruct + int64(len(sv.Fields))*allocStructField
}

func (mv *MapValue) GetShallowSize() int64 {
return allocMap + allocMapItem*int64(mv.GetLength())
}

func (bmv *BoundMethodValue) GetShallowSize() int64 {
return allocBoundMethod

Check warning on line 388 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L387-L388

Added lines #L387 - L388 were not covered by tests
}

func (hiv *HeapItemValue) GetShallowSize() int64 {
return allocHeapItem
}

func (rv RefValue) GetShallowSize() int64 {
return allocRefValue
}

func (pv PointerValue) GetShallowSize() int64 {
return allocPointer
}

func (sv *SliceValue) GetShallowSize() int64 {
return allocSlice
}

// Only count for closures.
func (fv *FuncValue) GetShallowSize() int64 {
return allocFunc +
int64(len(fv.Captures))*(allocTypedValue+allocHeapItem)
}

func (sv StringValue) GetShallowSize() int64 {
return allocString + allocStringByte*int64(len(sv))
}

func (bv BigintValue) GetShallowSize() int64 {
return allocBigint

Check warning on line 418 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L417-L418

Added lines #L417 - L418 were not covered by tests
}

func (bv BigdecValue) GetShallowSize() int64 {
return allocBigdec

Check warning on line 422 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L421-L422

Added lines #L421 - L422 were not covered by tests
}

func (dbv DataByteValue) GetShallowSize() int64 {
return allocDataByte

Check warning on line 426 in gnovm/pkg/gnolang/alloc.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnolang/alloc.go#L425-L426

Added lines #L425 - L426 were not covered by tests
}

// Do not count during recalculation,
// as the type should pre-exist.
func (tv TypeValue) GetShallowSize() int64 {
return 0
}
Loading
Loading