-
Notifications
You must be signed in to change notification settings - Fork 405
feat(gnovm): fill in 3779, the gc PR #3789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,18 @@ | ||
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 | ||
maxBytes int64 | ||
bytes int64 | ||
visitCount int64 // times objects are visited for gc | ||
gc func() (int64, bool) // gc callback | ||
} | ||
|
||
// for gonative, which doesn't consider the allocator. | ||
|
@@ -19,23 +25,27 @@ const ( | |
// gno types | ||
_allocSlice = 24 | ||
_allocPointerValue = 40 | ||
_allocStructValue = 152 | ||
_allocArrayValue = 176 | ||
_allocStringValue = 16 | ||
_allocStructValue = 160 | ||
_allocArrayValue = 184 | ||
_allocSliceValue = 40 | ||
_allocFuncValue = 136 | ||
_allocMapValue = 144 | ||
_allocBoundMethodValue = 176 | ||
_allocBlock = 464 | ||
_allocFuncValue = 196 | ||
_allocMapValue = 152 | ||
_allocBoundMethodValue = 184 | ||
_allocBlock = 480 | ||
_allocPackageValue = 248 | ||
_allocNativeValue = 48 | ||
_allocTypeValue = 16 | ||
_allocTypedValue = 40 | ||
_allocRefValue = 72 | ||
_allocBigint = 200 // XXX | ||
_allocBigdec = 200 // XXX | ||
_allocType = 200 // XXX | ||
_allocAny = 200 // XXX | ||
) | ||
|
||
const ( | ||
allocString = _allocBase | ||
allocString = _allocBase + _allocPointer + _allocStringValue | ||
allocStringByte = 1 | ||
allocBigint = _allocBase + _allocPointer + _allocBigint | ||
allocBigintByte = 1 | ||
|
@@ -53,12 +63,14 @@ const ( | |
allocBoundMethod = _allocBase + _allocPointer + _allocBoundMethodValue | ||
allocBlock = _allocBase + _allocPointer + _allocBlock | ||
allocBlockItem = _allocTypedValue | ||
allocNative = _allocBase + _allocPointer + _allocNativeValue | ||
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 | ||
allocAmino = _allocBase + _allocPointer + _allocAny | ||
allocAminoByte = 10 // XXX | ||
allocHeapItem = _allocBase + _allocPointer + _allocTypedValue | ||
) | ||
|
||
func NewAllocator(maxBytes int64) *Allocator { | ||
|
@@ -70,6 +82,18 @@ func NewAllocator(maxBytes int64) *Allocator { | |
} | ||
} | ||
|
||
func (alloc *Allocator) SetGCCallback(f func() (int64, bool)) { | ||
alloc.gc = f | ||
} | ||
|
||
func (alloc *Allocator) MemStats() string { | ||
if alloc == nil { | ||
return "nil allocator" | ||
} 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 | ||
} | ||
|
@@ -100,7 +124,17 @@ func (alloc *Allocator) Allocate(size int64) { | |
|
||
alloc.bytes += size | ||
if alloc.bytes > alloc.maxBytes { | ||
panic("allocation limit exceeded") | ||
if left, ok := alloc.gc(); !ok { | ||
panic("should not happen, allocation limit exceeded while gc.") | ||
} else { // retry | ||
if debug { | ||
debug.Printf("%d left after GC, required size: %d\n", left, size) | ||
} | ||
alloc.bytes += size | ||
if alloc.bytes > alloc.maxBytes { | ||
panic("allocation limit exceeded") | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
@@ -299,3 +333,80 @@ func (alloc *Allocator) NewHeapItem(tv TypedValue) *HeapItemValue { | |
alloc.AllocateHeapItem() | ||
return &HeapItemValue{Value: tv} | ||
} | ||
|
||
// ----------------------------------------------- | ||
|
||
func (pv *PackageValue) GetShallowSize() int64 { | ||
return allocPackage | ||
} | ||
|
||
func (b *Block) GetShallowSize() int64 { | ||
return allocBlock | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, see AllocateBlock. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed. |
||
} | ||
|
||
func (av *ArrayValue) GetShallowSize() int64 { | ||
if av.Data != nil { | ||
return allocArray + int64(len(av.Data)) | ||
} else { | ||
return allocArray | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here for the else clause. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed. |
||
} | ||
} | ||
|
||
func (sv *StructValue) GetShallowSize() int64 { | ||
return allocStruct | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and so on... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed. |
||
} | ||
|
||
func (mv *MapValue) GetShallowSize() int64 { | ||
return allocMap | ||
} | ||
|
||
func (bmv *BoundMethodValue) GetShallowSize() int64 { | ||
return allocBoundMethod | ||
} | ||
|
||
func (hiv *HeapItemValue) GetShallowSize() int64 { | ||
return allocHeapItem | ||
} | ||
|
||
func (rv RefValue) GetShallowSize() int64 { | ||
fmt.Println("---refValue, get shallow size...") | ||
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 { | ||
if fv.IsClosure { | ||
return allocFunc | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems wrong, should be allocFunc no matter what I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems the function is defined in preprocess and copied during prepareNewValues, before runtime—so there shouldn’t be any runtime allocation. Am I mistaken? |
||
return 0 | ||
} | ||
|
||
func (sv StringValue) GetShallowSize() int64 { | ||
return allocString + allocStringByte*int64(len(sv)) | ||
} | ||
|
||
func (bv BigintValue) GetShallowSize() int64 { | ||
return allocBigint | ||
} | ||
|
||
func (bv BigdecValue) GetShallowSize() int64 { | ||
return allocBigdec | ||
} | ||
|
||
func (dbv DataByteValue) GetShallowSize() int64 { | ||
return allocDataByte | ||
} | ||
|
||
// Do not count during recalculation, | ||
// as the type should pre-exist. | ||
func (tv TypeValue) GetShallowSize() int64 { | ||
return 0 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these should account for all typedvalues as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is, the TypedValue array size should be included in the block, even if each value itself might have its own allocation on top.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.