From af823da6277221e3b81ce30bd506b1ef9d72091b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=9A=93=E6=99=9F?= <2067144018@qq.com> Date: Sun, 23 Nov 2025 15:26:02 +0800 Subject: [PATCH] [no ci] backup --- include/pocketpy/interpreter/heap.h | 17 +++--- include/typings/pkpy.pyi | 4 +- src/interpreter/heap.c | 95 +++++++++++++++++++++++++++-- src/interpreter/vm.c | 2 + src/interpreter/vmx.c | 16 +++-- src/modules/pkpy.c | 12 ++++ 6 files changed, 128 insertions(+), 18 deletions(-) diff --git a/include/pocketpy/interpreter/heap.h b/include/pocketpy/interpreter/heap.h index 735e9e83..60fd7c46 100644 --- a/include/pocketpy/interpreter/heap.h +++ b/include/pocketpy/interpreter/heap.h @@ -13,19 +13,22 @@ typedef struct ManagedHeap { int gc_threshold; // threshold for gc_counter int gc_counter; // objects created since last gc bool gc_enabled; + py_Ref debug_callback; } ManagedHeap; typedef struct { clock_t start; - clock_t end; - + clock_t mark_end; + clock_t swpet_end; + + int types_length; int* small_types; int* large_types; + int small_freed; int large_freed; struct { - bool valid; int before; int after; int upper; @@ -38,11 +41,11 @@ typedef struct { void ManagedHeap__ctor(ManagedHeap* self); void ManagedHeap__dtor(ManagedHeap* self); -void ManagedHeapSwpetInfo__ctor(ManagedHeapSwpetInfo* self); -void ManagedHeapSwpetInfo__dtor(ManagedHeapSwpetInfo* self); +ManagedHeapSwpetInfo* ManagedHeapSwpetInfo__new(); +void ManagedHeapSwpetInfo__delete(ManagedHeapSwpetInfo* self); -void ManagedHeap__collect_if_needed(ManagedHeap* self, ManagedHeapSwpetInfo* out_info); -int ManagedHeap__collect(ManagedHeap* self, ManagedHeapSwpetInfo* out_info); +void ManagedHeap__collect_if_needed(ManagedHeap* self); +int ManagedHeap__collect(ManagedHeap* self); int ManagedHeap__sweep(ManagedHeap* self, ManagedHeapSwpetInfo* out_info); #define ManagedHeap__new(self, type, slots, udsize) \ diff --git a/include/typings/pkpy.pyi b/include/typings/pkpy.pyi index 50e05129..7648542b 100644 --- a/include/typings/pkpy.pyi +++ b/include/typings/pkpy.pyi @@ -1,4 +1,4 @@ -from typing import Self, Literal +from typing import Self, Literal, Callable from vmath import vec2, vec2i class TValue[T]: @@ -16,6 +16,8 @@ configmacros: dict[str, int] def memory_usage() -> str: """Return a summary of the memory usage.""" +def setup_gc_debug_callback(cb: Callable[[str], None]) -> None: + """Setup a callback that will be triggered at the end of GC.""" def is_user_defined_type(t: type) -> bool: """Check if a type is user-defined. This means the type was created by executing python `class` statement.""" diff --git a/src/interpreter/heap.c b/src/interpreter/heap.c index 941e9dcb..6f305243 100644 --- a/src/interpreter/heap.c +++ b/src/interpreter/heap.c @@ -2,6 +2,7 @@ #include "pocketpy/config.h" #include "pocketpy/interpreter/objectpool.h" #include "pocketpy/objects/base.h" +#include "pocketpy/common/sstream.h" #include "pocketpy/pocketpy.h" #include @@ -16,6 +17,7 @@ void ManagedHeap__ctor(ManagedHeap* self) { self->gc_threshold = PK_GC_MIN_THRESHOLD; self->gc_counter = 0; self->gc_enabled = true; + self->debug_callback = NULL; } void ManagedHeap__dtor(ManagedHeap* self) { @@ -31,10 +33,76 @@ void ManagedHeap__dtor(ManagedHeap* self) { c11_vector__dtor(&self->gc_roots); } -void ManagedHeap__collect_if_needed(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) { +static void ManagedHeap__fire_debug_callback(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) { + assert(self->debug_callback != NULL); + assert(out_info != NULL); + + c11_sbuf buf; + c11_sbuf__ctor(&buf); + + const clock_t CLOCKS_PER_MS = CLOCKS_PER_SEC / 1000; + const char* DIVIDER = "------------------------------"; + + clock_t start = out_info->start / CLOCKS_PER_MS; + clock_t mark_ms = (out_info->mark_end - out_info->start) / CLOCKS_PER_MS; + clock_t swpet_ms = (out_info->swpet_end - out_info->mark_end) / CLOCKS_PER_MS; + + 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); + + 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_Ref p0 = py_peek(0); + 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) py_clearexc(p0); // noexcept +} + +void ManagedHeap__collect_if_needed(ManagedHeap* self) { if(!self->gc_enabled) return; if(self->gc_counter < self->gc_threshold) return; - int freed = ManagedHeap__collect(self, out_info); + self->gc_counter = 0; + + ManagedHeapSwpetInfo* out_info = NULL; + if(self->debug_callback) out_info = ManagedHeapSwpetInfo__new(); + + ManagedHeap__mark(self); + if(out_info) out_info->mark_end = clock(); + int freed = ManagedHeap__sweep(self, out_info); + if(out_info) out_info->swpet_end = clock(); + // adjust `gc_threshold` based on `freed_ma` self->freed_ma[0] = self->freed_ma[1]; self->freed_ma[1] = self->freed_ma[2]; @@ -45,7 +113,6 @@ void ManagedHeap__collect_if_needed(ManagedHeap* self, ManagedHeapSwpetInfo* out 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.valid = true; out_info->auto_thres.before = self->gc_threshold; out_info->auto_thres.after = new_threshold; out_info->auto_thres.upper = upper; @@ -54,12 +121,29 @@ void ManagedHeap__collect_if_needed(ManagedHeap* self, ManagedHeapSwpetInfo* out out_info->auto_thres.free_ratio = free_ratio; } self->gc_threshold = c11__min(c11__max(new_threshold, lower), upper); + + if(self->debug_callback) { + ManagedHeap__fire_debug_callback(self, out_info); + ManagedHeapSwpetInfo__delete(out_info); + } } -int ManagedHeap__collect(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) { +int ManagedHeap__collect(ManagedHeap* self) { self->gc_counter = 0; + + ManagedHeapSwpetInfo* out_info = NULL; + if(self->debug_callback) out_info = ManagedHeapSwpetInfo__new(); + ManagedHeap__mark(self); - return ManagedHeap__sweep(self, out_info); + if(out_info) out_info->mark_end = clock(); + int freed = ManagedHeap__sweep(self, out_info); + if(out_info) out_info->swpet_end = clock(); + + if(self->debug_callback) { + ManagedHeap__fire_debug_callback(self, out_info); + ManagedHeapSwpetInfo__delete(out_info); + } + return freed; } int ManagedHeap__sweep(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) { @@ -86,7 +170,6 @@ int ManagedHeap__sweep(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) { if(out_info) { out_info->small_freed = small_freed; out_info->large_freed = large_freed; - out_info->end = clock(); } return small_freed + large_freed; } diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 526f165e..308d1153 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -685,6 +685,8 @@ void ManagedHeap__mark(ManagedHeap* self) { for(int i = 0; i < c11__count_array(vm->reg); i++) { pk__mark_value(&vm->reg[i]); } + // mark gc debug callback + if(vm->heap.debug_callback) pk__mark_value(vm->heap.debug_callback); // mark user func if(vm->callbacks.gc_mark) vm->callbacks.gc_mark(pk__mark_value_func, p_stack); /*****************************/ diff --git a/src/interpreter/vmx.c b/src/interpreter/vmx.c index 5dfa964d..c7b4c336 100644 --- a/src/interpreter/vmx.c +++ b/src/interpreter/vmx.c @@ -130,15 +130,23 @@ void PyObject__dtor(PyObject* self) { } } -void ManagedHeapSwpetInfo__ctor(ManagedHeapSwpetInfo* self) { +ManagedHeapSwpetInfo* ManagedHeapSwpetInfo__new() { + ManagedHeapSwpetInfo* self = py_malloc(sizeof(ManagedHeapSwpetInfo)); memset(self, 0, sizeof(ManagedHeapSwpetInfo)); self->start = clock(); - self->small_types = py_malloc(sizeof(int) * pk_current_vm->types.length); - self->large_types = py_malloc(sizeof(int) * pk_current_vm->types.length); + self->types_length = pk_current_vm->types.length; + self->small_types = py_malloc(sizeof(int) * self->types_length); + self->large_types = py_malloc(sizeof(int) * self->types_length); + for(int i = 0; i < self->types_length; i++) { + self->small_types[i] = 0; + self->large_types[i] = 0; + } + return self; } -void ManagedHeapSwpetInfo__dtor(ManagedHeapSwpetInfo* self) { +void ManagedHeapSwpetInfo__delete(ManagedHeapSwpetInfo* self) { py_free(self->small_types); py_free(self->large_types); memset(self, 0, sizeof(ManagedHeapSwpetInfo)); + py_free(self); } diff --git a/src/modules/pkpy.c b/src/modules/pkpy.c index 0d99ecb3..32961db8 100644 --- a/src/modules/pkpy.c +++ b/src/modules/pkpy.c @@ -57,6 +57,17 @@ static bool pkpy_memory_usage(int argc, py_Ref argv) { return true; } +static bool pkpy_setup_gc_debug_callback(int argc, py_Ref argv) { + PY_CHECK_ARGC(0); + ManagedHeap* heap = &pk_current_vm->heap; + if(py_isnone(argv)) { + heap->debug_callback = NULL; + } else { + heap->debug_callback = argv; + } + return true; +} + static bool pkpy_is_user_defined_type(int argc, py_Ref argv) { PY_CHECK_ARGC(1); PY_CHECK_ARG_TYPE(0, tp_type); @@ -530,6 +541,7 @@ void pk__add_module_pkpy() { py_pop(); py_bindfunc(mod, "memory_usage", pkpy_memory_usage); + py_bindfunc(mod, "setup_gc_debug_callback", pkpy_setup_gc_debug_callback); py_bindfunc(mod, "is_user_defined_type", pkpy_is_user_defined_type); py_bindfunc(mod, "currentvm", pkpy_currentvm);