mirror of
https://github.com/pocketpy/pocketpy
synced 2025-12-06 10:10:17 +00:00
210 lines
7.7 KiB
C
210 lines
7.7 KiB
C
#include "pocketpy/interpreter/heap.h"
|
|
#include "pocketpy/config.h"
|
|
#include "pocketpy/interpreter/objectpool.h"
|
|
#include "pocketpy/objects/base.h"
|
|
#include "pocketpy/common/sstream.h"
|
|
#include "pocketpy/pocketpy.h"
|
|
#include <assert.h>
|
|
|
|
|
|
void ManagedHeap__ctor(ManagedHeap* self) {
|
|
MultiPool__ctor(&self->small_objects);
|
|
c11_vector__ctor(&self->large_objects, sizeof(PyObject*));
|
|
c11_vector__ctor(&self->gc_roots, sizeof(PyObject*));
|
|
|
|
for(int i = 0; i < c11__count_array(self->freed_ma); i++) {
|
|
self->freed_ma[i] = PK_GC_MIN_THRESHOLD;
|
|
}
|
|
self->gc_threshold = PK_GC_MIN_THRESHOLD;
|
|
self->gc_counter = 0;
|
|
self->gc_enabled = true;
|
|
self->debug_callback = *py_None();
|
|
}
|
|
|
|
void ManagedHeap__dtor(ManagedHeap* self) {
|
|
// small_objects
|
|
MultiPool__dtor(&self->small_objects);
|
|
// large_objects
|
|
for(int i = 0; i < self->large_objects.length; i++) {
|
|
PyObject* obj = c11__getitem(PyObject*, &self->large_objects, i);
|
|
PyObject__dtor(obj);
|
|
PK_FREE(obj);
|
|
}
|
|
c11_vector__dtor(&self->large_objects);
|
|
c11_vector__dtor(&self->gc_roots);
|
|
}
|
|
|
|
static void ManagedHeap__fire_debug_callback(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) {
|
|
assert(out_info != NULL);
|
|
|
|
c11_sbuf buf;
|
|
c11_sbuf__ctor(&buf);
|
|
|
|
const int64_t NANOS_PER_SEC = 1000000000;
|
|
const char* DIVIDER = "------------------------------------------------------------\n";
|
|
|
|
double start = out_info->start_ns / 1e9;
|
|
int64_t mark_ms = (out_info->mark_end_ns - out_info->start_ns) / NANOS_PER_SEC;
|
|
int64_t swpet_ms = (out_info->swpet_end_ns - out_info->mark_end_ns) / NANOS_PER_SEC;
|
|
|
|
c11_sbuf__write_cstr(&buf, DIVIDER);
|
|
pk_sprintf(&buf, "start: %f\n", (double)start / 1000);
|
|
pk_sprintf(&buf, "mark_ms: %i\n", (py_i64)mark_ms);
|
|
pk_sprintf(&buf, "swpet_ms: %i\n", (py_i64)swpet_ms);
|
|
pk_sprintf(&buf, "total_ms: %i\n", (py_i64)(mark_ms + swpet_ms));
|
|
c11_sbuf__write_cstr(&buf, DIVIDER);
|
|
pk_sprintf(&buf, "types_length: %d\n", out_info->types_length);
|
|
pk_sprintf(&buf, "small_freed: %d\n", out_info->small_freed);
|
|
pk_sprintf(&buf, "large_freed: %d\n", out_info->large_freed);
|
|
c11_sbuf__write_cstr(&buf, DIVIDER);
|
|
|
|
if(out_info->small_freed != 0 || out_info->large_freed != 0) {
|
|
char line_buf[256];
|
|
for(int i = 0; i < out_info->types_length; i++) {
|
|
const char* type_name = py_tpname(i);
|
|
int s_freed = out_info->small_types[i];
|
|
int l_freed = out_info->large_types[i];
|
|
if(s_freed == 0 && l_freed == 0) continue;
|
|
snprintf(line_buf,
|
|
sizeof(line_buf),
|
|
"[%-24s] small: %6d large: %6d\n",
|
|
type_name,
|
|
s_freed,
|
|
l_freed);
|
|
c11_sbuf__write_cstr(&buf, line_buf);
|
|
}
|
|
c11_sbuf__write_cstr(&buf, DIVIDER);
|
|
}
|
|
|
|
pk_sprintf(&buf, "auto_thres.before: %d\n", out_info->auto_thres.before);
|
|
pk_sprintf(&buf, "auto_thres.after: %d\n", out_info->auto_thres.after);
|
|
pk_sprintf(&buf, "auto_thres.upper: %d\n", out_info->auto_thres.upper);
|
|
pk_sprintf(&buf, "auto_thres.lower: %d\n", out_info->auto_thres.lower);
|
|
pk_sprintf(&buf, "auto_thres.avg_freed: %d\n", out_info->auto_thres.avg_freed);
|
|
pk_sprintf(&buf, "auto_thres.free_ratio: %f\n", out_info->auto_thres.free_ratio);
|
|
c11_sbuf__write_cstr(&buf, DIVIDER);
|
|
|
|
py_push(&self->debug_callback);
|
|
py_pushnil();
|
|
py_StackRef arg = py_pushtmp();
|
|
c11_sbuf__py_submit(&buf, arg);
|
|
bool ok = py_vectorcall(1, 0);
|
|
if(!ok) {
|
|
char* msg = py_formatexc();
|
|
c11__abort("gc_debug_callback error!!\n%s", msg);
|
|
}
|
|
}
|
|
|
|
void ManagedHeap__collect_hint(ManagedHeap* self) {
|
|
if(self->gc_counter < self->gc_threshold) return;
|
|
self->gc_counter = 0;
|
|
|
|
ManagedHeapSwpetInfo* out_info = NULL;
|
|
if(!py_isnone(&self->debug_callback)) out_info = ManagedHeapSwpetInfo__new();
|
|
|
|
ManagedHeap__mark(self);
|
|
if(out_info) out_info->mark_end_ns = time_ns();
|
|
int freed = ManagedHeap__sweep(self, out_info);
|
|
if(out_info) out_info->swpet_end_ns = time_ns();
|
|
|
|
// adjust `gc_threshold` based on `freed_ma`
|
|
self->freed_ma[0] = self->freed_ma[1];
|
|
self->freed_ma[1] = self->freed_ma[2];
|
|
self->freed_ma[2] = freed;
|
|
int avg_freed = (self->freed_ma[0] + self->freed_ma[1] + self->freed_ma[2]) / 3;
|
|
const int upper = PK_GC_MIN_THRESHOLD * 16;
|
|
const int lower = PK_GC_MIN_THRESHOLD / 2;
|
|
float free_ratio = (float)avg_freed / self->gc_threshold;
|
|
int new_threshold = self->gc_threshold * (1.5f / free_ratio);
|
|
if(out_info) {
|
|
out_info->auto_thres.before = self->gc_threshold;
|
|
out_info->auto_thres.after = new_threshold;
|
|
out_info->auto_thres.upper = upper;
|
|
out_info->auto_thres.lower = lower;
|
|
out_info->auto_thres.avg_freed = avg_freed;
|
|
out_info->auto_thres.free_ratio = free_ratio;
|
|
}
|
|
self->gc_threshold = c11__min(c11__max(new_threshold, lower), upper);
|
|
|
|
if(!py_isnone(&self->debug_callback)) {
|
|
ManagedHeap__fire_debug_callback(self, out_info);
|
|
ManagedHeapSwpetInfo__delete(out_info);
|
|
}
|
|
}
|
|
|
|
int ManagedHeap__collect(ManagedHeap* self) {
|
|
self->gc_counter = 0;
|
|
|
|
ManagedHeapSwpetInfo* out_info = NULL;
|
|
if(!py_isnone(&self->debug_callback)) out_info = ManagedHeapSwpetInfo__new();
|
|
|
|
ManagedHeap__mark(self);
|
|
if(out_info) out_info->mark_end_ns = time_ns();
|
|
int freed = ManagedHeap__sweep(self, out_info);
|
|
if(out_info) out_info->swpet_end_ns = time_ns();
|
|
|
|
if(out_info) {
|
|
out_info->auto_thres.before = self->gc_threshold;
|
|
out_info->auto_thres.after = self->gc_threshold;
|
|
}
|
|
|
|
if(!py_isnone(&self->debug_callback)) {
|
|
ManagedHeap__fire_debug_callback(self, out_info);
|
|
ManagedHeapSwpetInfo__delete(out_info);
|
|
}
|
|
return freed;
|
|
}
|
|
|
|
int ManagedHeap__sweep(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) {
|
|
// small_objects
|
|
int small_freed =
|
|
MultiPool__sweep_dealloc(&self->small_objects, out_info ? out_info->small_types : NULL);
|
|
// large_objects
|
|
int large_living_count = 0;
|
|
for(int i = 0; i < self->large_objects.length; i++) {
|
|
PyObject* obj = c11__getitem(PyObject*, &self->large_objects, i);
|
|
if(obj->gc_marked) {
|
|
obj->gc_marked = false;
|
|
c11__setitem(PyObject*, &self->large_objects, large_living_count, obj);
|
|
large_living_count++;
|
|
} else {
|
|
if(out_info) out_info->large_types[obj->type]++;
|
|
PyObject__dtor(obj);
|
|
PK_FREE(obj);
|
|
}
|
|
}
|
|
// shrink `self->large_objects`
|
|
int large_freed = self->large_objects.length - large_living_count;
|
|
self->large_objects.length = large_living_count;
|
|
if(out_info) {
|
|
out_info->small_freed = small_freed;
|
|
out_info->large_freed = large_freed;
|
|
}
|
|
return small_freed + large_freed;
|
|
}
|
|
|
|
PyObject* ManagedHeap__gcnew(ManagedHeap* self, py_Type type, int slots, int udsize) {
|
|
assert(slots >= 0 || slots == -1);
|
|
// header + slots + udsize
|
|
int size = sizeof(PyObject) + PK_OBJ_SLOTS_SIZE(slots) + udsize;
|
|
PyObject* obj = MultiPool__alloc(&self->small_objects, size);
|
|
if(obj == NULL) {
|
|
obj = PK_MALLOC(size);
|
|
c11_vector__push(PyObject*, &self->large_objects, obj);
|
|
}
|
|
obj->type = type;
|
|
obj->gc_marked = false;
|
|
obj->slots = slots;
|
|
|
|
// initialize slots or dict
|
|
if(slots >= 0) {
|
|
memset(obj->flex, 0, slots * sizeof(py_TValue));
|
|
} else {
|
|
float load_factor = (type == tp_type || type == tp_module) ? PK_TYPE_ATTR_LOAD_FACTOR
|
|
: PK_INST_ATTR_LOAD_FACTOR;
|
|
NameDict__ctor((void*)obj->flex, load_factor);
|
|
}
|
|
|
|
self->gc_counter++;
|
|
return obj;
|
|
} |