diff --git a/README.md b/README.md index dc4ec8d1..4d8b6434 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Please see https://pocketpy.dev for details and try the following resources. pkpy should work on any platform with a C11 compiler. These platforms are officially tested. -> C99 compilers also work currently according to users' feedback. +> C99 compilers may also work currently according to users' feedback. + Windows 64-bit + Linux 64-bit / 32-bit @@ -74,7 +74,7 @@ It is safe to use `main` branch in production if CI badge is green. To compile it with your project, these flags must be set: -+ `--std=c11` flag must be set (`--std=c99` may also work) ++ `--std=c11` flag must be set + For MSVC, `/utf-8` flag must be set + `NDEBUG` macro should be defined for release build, or you will get poor performance diff --git a/docs/index.md b/docs/index.md index 21794657..550f106b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,7 +32,7 @@ print(primes) pkpy should work on any platform with a C11 compiler. These platforms are officially tested. -> C99 compilers also work currently according to users' feedback. +> C99 compilers may also work currently according to users' feedback. + Windows 64-bit + Linux 64-bit / 32-bit diff --git a/docs/quick-start.md b/docs/quick-start.md index 99b4b0c2..a585e3ab 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -29,7 +29,7 @@ It is safe to use `main` branch in production if CI badge is green. To compile it with your project, these flags must be set: -+ `--std=c11` flag must be set (`--std=c99` may also work) ++ `--std=c11` flag must be set + For MSVC, `/utf-8` flag must be set + `NDEBUG` macro should be defined for release build, or you will get poor performance diff --git a/docs/retype.yml b/docs/retype.yml index c702101d..ef382904 100644 --- a/docs/retype.yml +++ b/docs/retype.yml @@ -3,7 +3,7 @@ output: .retype url: https://pocketpy.dev branding: title: pocketpy - label: v2.0.5 + label: v2.0.6 logo: "./static/logo.png" favicon: "./static/logo.png" meta: diff --git a/include/pocketpy/common/memorypool.h b/include/pocketpy/common/memorypool.h index 4afc6fb1..49a542ff 100644 --- a/include/pocketpy/common/memorypool.h +++ b/include/pocketpy/common/memorypool.h @@ -2,10 +2,6 @@ #define kPoolExprBlockSize 128 #define kPoolFrameBlockSize 80 -#define kPoolObjectBlockSize 80 - -#define kPoolObjectArenaSize (256*1024) -#define kPoolObjectMaxBlocks (kPoolObjectArenaSize / kPoolObjectBlockSize) void MemoryPools__initialize(); void MemoryPools__finalize(); @@ -14,9 +10,3 @@ void* PoolExpr_alloc(); void PoolExpr_dealloc(void*); void* PoolFrame_alloc(); void PoolFrame_dealloc(void*); - -void* PoolObject_alloc(); -void PoolObject_dealloc(void* p); -void PoolObject_shrink_to_fit(); - -void Pools_debug_info(char* buffer, int size); diff --git a/include/pocketpy/common/vector.h b/include/pocketpy/common/vector.h index 836c792e..a37ae111 100644 --- a/include/pocketpy/common/vector.h +++ b/include/pocketpy/common/vector.h @@ -22,6 +22,7 @@ void c11_vector__clear(c11_vector* self); void* c11_vector__emplace(c11_vector* self); bool c11_vector__contains(const c11_vector* self, void* elem); void* c11_vector__submit(c11_vector* self, int* length); +void c11_vector__swap(c11_vector* self, c11_vector* other); #define c11__getitem(T, self, index) (((T*)(self)->data)[index]) #define c11__setitem(T, self, index, value) ((T*)(self)->data)[index] = value; diff --git a/include/pocketpy/config.h b/include/pocketpy/config.h index f72144b8..e512ffa5 100644 --- a/include/pocketpy/config.h +++ b/include/pocketpy/config.h @@ -1,10 +1,10 @@ #pragma once // clang-format off -#define PK_VERSION "2.0.5" +#define PK_VERSION "2.0.6" #define PK_VERSION_MAJOR 2 #define PK_VERSION_MINOR 0 -#define PK_VERSION_PATCH 5 +#define PK_VERSION_PATCH 6 /*************** feature settings ***************/ diff --git a/include/pocketpy/interpreter/heap.h b/include/pocketpy/interpreter/heap.h index 03986143..c997fd22 100644 --- a/include/pocketpy/interpreter/heap.h +++ b/include/pocketpy/interpreter/heap.h @@ -1,24 +1,25 @@ #include "pocketpy/objects/object.h" +#include "pocketpy/interpreter/objectpool.h" -typedef struct ManagedHeap{ - c11_vector no_gc; - c11_vector gen; +typedef struct ManagedHeap { + MultiPool small_objects; + c11_vector large_objects; - int gc_threshold; - int gc_counter; + int freed_ma[3]; + int gc_threshold; // threshold for gc_counter + int gc_counter; // objects created since last gc bool gc_enabled; - - VM* vm; } ManagedHeap; -void ManagedHeap__ctor(ManagedHeap* self, VM* vm); +void ManagedHeap__ctor(ManagedHeap* self); void ManagedHeap__dtor(ManagedHeap* self); void ManagedHeap__collect_if_needed(ManagedHeap* self); int ManagedHeap__collect(ManagedHeap* self); int ManagedHeap__sweep(ManagedHeap* self); -PyObject* ManagedHeap__new(ManagedHeap* self, py_Type type, int slots, int udsize); +#define ManagedHeap__new(self, type, slots, udsize) \ + ManagedHeap__gcnew((self), (type), (slots), (udsize)) PyObject* ManagedHeap__gcnew(ManagedHeap* self, py_Type type, int slots, int udsize); // external implementation diff --git a/include/pocketpy/interpreter/objectpool.h b/include/pocketpy/interpreter/objectpool.h new file mode 100644 index 00000000..51d06d49 --- /dev/null +++ b/include/pocketpy/interpreter/objectpool.h @@ -0,0 +1,30 @@ +#pragma once + +#include "pocketpy/common/vector.h" + +#define kPoolArenaSize (120 * 1024) +#define kMultiPoolCount 5 +#define kPoolMaxBlockSize (32*kMultiPoolCount) + +typedef struct PoolArena { + int block_size; + int block_count; + int unused_count; + int* unused; + char data[kPoolArenaSize]; +} PoolArena; + +typedef struct Pool { + c11_vector /* PoolArena* */ arenas; + c11_vector /* PoolArena* */ not_free_arenas; + int block_size; +} Pool; + +typedef struct MultiPool { + Pool pools[kMultiPoolCount]; +} MultiPool; + +void* MultiPool__alloc(MultiPool* self, int size); +int MultiPool__sweep_dealloc(MultiPool* self); +void MultiPool__ctor(MultiPool* self); +void MultiPool__dtor(MultiPool* self); diff --git a/include/pocketpy/objects/object.h b/include/pocketpy/objects/object.h index 622dc187..822df540 100644 --- a/include/pocketpy/objects/object.h +++ b/include/pocketpy/objects/object.h @@ -5,7 +5,7 @@ typedef struct PyObject { py_Type type; // we have a duplicated type here for convenience - bool gc_is_large; + // bool _; bool gc_marked; int slots; // number of slots in the object char flex[]; @@ -23,5 +23,4 @@ void* PyObject__userdata(PyObject* self); #define PK_OBJ_SLOTS_SIZE(slots) ((slots) >= 0 ? sizeof(py_TValue) * (slots) : sizeof(NameDict)) -PyObject* PyObject__new(py_Type type, int slots, int size); -void PyObject__delete(PyObject* self); +void PyObject__dtor(PyObject* self); diff --git a/plugins/flutter/pocketpy/pubspec.yaml b/plugins/flutter/pocketpy/pubspec.yaml index 798f767d..b6200886 100644 --- a/plugins/flutter/pocketpy/pubspec.yaml +++ b/plugins/flutter/pocketpy/pubspec.yaml @@ -1,6 +1,6 @@ name: pocketpy description: A lightweight Python interpreter for game engines. It supports Android/iOS/Windows/Linux/MacOS. -version: 2.0.5 +version: 2.0.6 homepage: https://pocketpy.dev repository: https://github.com/pocketpy/pocketpy diff --git a/src/common/memorypool.c b/src/common/memorypool.c index 5c09861b..76c24723 100644 --- a/src/common/memorypool.c +++ b/src/common/memorypool.c @@ -2,183 +2,8 @@ #include "pocketpy/pocketpy.h" #include -#include -#include -#include #include -typedef struct LinkedListNode { - struct LinkedListNode* prev; - struct LinkedListNode* next; -} LinkedListNode; - -typedef struct LinkedList { - int length; - LinkedListNode head; - LinkedListNode tail; -} LinkedList; - -static void LinkedList__ctor(LinkedList* self) { - self->length = 0; - self->head.prev = NULL; - self->head.next = &self->tail; - self->tail.prev = &self->head; - self->tail.next = NULL; -} - -static void LinkedList__push_back(LinkedList* self, LinkedListNode* node) { - node->prev = self->tail.prev; - node->next = &self->tail; - self->tail.prev->next = node; - self->tail.prev = node; - self->length++; -} - -static void LinkedList__push_front(LinkedList* self, LinkedListNode* node) { - node->prev = &self->head; - node->next = self->head.next; - self->head.next->prev = node; - self->head.next = node; - self->length++; -} - -static void LinkedList__pop_back(LinkedList* self) { - assert(self->length > 0); - self->tail.prev->prev->next = &self->tail; - self->tail.prev = self->tail.prev->prev; - self->length--; -} - -static LinkedListNode* LinkedList__back(LinkedList* self) { - assert(self->length > 0); - return self->tail.prev; -} - -static void LinkedList__erase(LinkedList* self, LinkedListNode* node) { - node->prev->next = node->next; - node->next->prev = node->prev; - self->length--; -} - -#define LinkedList__apply(self, __STATEMENTS__) \ - do { \ - LinkedListNode* node = (self)->head.next; \ - while(node != &(self)->tail) { \ - LinkedListNode* next = node->next; \ - __STATEMENTS__ \ - node = next; \ - } \ - } while(0) - -typedef struct MemoryPoolBlock{ - void* arena; - char data[kPoolObjectBlockSize]; -} MemoryPoolBlock; - -typedef struct MemoryPoolArena{ - /* LinkedListNode */ - LinkedListNode* prev; - LinkedListNode* next; - /* Arena */ - MemoryPoolBlock _blocks[kPoolObjectMaxBlocks]; - MemoryPoolBlock* _free_list[kPoolObjectMaxBlocks]; - int _free_list_size; -} MemoryPoolArena; - -typedef struct MemoryPool{ - LinkedList _arenas; - LinkedList _empty_arenas; -} MemoryPool; - -static void MemoryPoolArena__ctor(MemoryPoolArena* self) { - self->prev = NULL; - self->next = NULL; - self->_free_list_size = kPoolObjectMaxBlocks; - for(int i = 0; i < kPoolObjectMaxBlocks; i++) { - self->_blocks[i].arena = self; - self->_free_list[i] = &self->_blocks[i]; - } -} - -static bool MemoryPoolArena__empty(MemoryPoolArena* self) { - return self->_free_list_size == 0; -} - -static bool MemoryPoolArena__full(MemoryPoolArena* self) { - return self->_free_list_size == kPoolObjectMaxBlocks; -} - -static int MemoryPoolArena__total_bytes(MemoryPoolArena* self) { - return kPoolObjectArenaSize; -} - -static int MemoryPoolArena__used_bytes(MemoryPoolArena* self) { - return kPoolObjectBlockSize * (kPoolObjectMaxBlocks - self->_free_list_size); -} - -static MemoryPoolBlock* MemoryPoolArena__alloc(MemoryPoolArena* self) { - assert(!MemoryPoolArena__empty(self)); - self->_free_list_size--; - return self->_free_list[self->_free_list_size]; -} - -static void MemoryPoolArena__dealloc(MemoryPoolArena* self, MemoryPoolBlock* block) { - assert(!MemoryPoolArena__full(self)); - self->_free_list[self->_free_list_size] = block; - self->_free_list_size++; -} - -static void MemoryPool__ctor(MemoryPool* self) { - LinkedList__ctor(&self->_arenas); - LinkedList__ctor(&self->_empty_arenas); -} - -static void* MemoryPool__alloc(MemoryPool* self) { - MemoryPoolArena* arena; - if(self->_arenas.length == 0){ - arena = PK_MALLOC(sizeof(MemoryPoolArena)); - MemoryPoolArena__ctor(arena); - LinkedList__push_back(&self->_arenas, (LinkedListNode*)arena); - } else { - arena = (MemoryPoolArena*)LinkedList__back(&self->_arenas); - } - void* p = MemoryPoolArena__alloc(arena)->data; - if(MemoryPoolArena__empty(arena)) { - LinkedList__pop_back(&self->_arenas); - LinkedList__push_back(&self->_empty_arenas, (LinkedListNode*)arena); - } - return p; -} - -static void MemoryPool__dealloc(MemoryPool* self, void* p) { - assert(p != NULL); - MemoryPoolBlock* block = (MemoryPoolBlock*)((char*)p - sizeof(void*)); - assert(block->arena != NULL); - MemoryPoolArena* arena = (MemoryPoolArena*)block->arena; - if(MemoryPoolArena__empty(arena)) { - LinkedList__erase(&self->_empty_arenas, (LinkedListNode*)arena); - LinkedList__push_front(&self->_arenas, (LinkedListNode*)arena); - } - MemoryPoolArena__dealloc(arena, block); -} - -static void MemoryPool__shrink_to_fit(MemoryPool* self) { - const int MIN_ARENA_COUNT = PK_GC_MIN_THRESHOLD * 100 / (kPoolObjectArenaSize); - if(self->_arenas.length < MIN_ARENA_COUNT) return; - LinkedList__apply(&self->_arenas, - MemoryPoolArena* arena = (MemoryPoolArena*)node; - if(MemoryPoolArena__full(arena)) { - LinkedList__erase(&self->_arenas, node); - PK_FREE(arena); - }); -} - - -static void MemoryPool__dtor(MemoryPool* self) { - LinkedList__apply(&self->_arenas, PK_FREE(node);); - LinkedList__apply(&self->_empty_arenas, PK_FREE(node);); -} - typedef struct FixedMemoryPool { int BlockSize; int BlockCount; @@ -240,18 +65,15 @@ static int FixedMemoryPool__total_bytes(FixedMemoryPool* self) { static FixedMemoryPool PoolExpr; static FixedMemoryPool PoolFrame; -static MemoryPool PoolObject; void MemoryPools__initialize(){ FixedMemoryPool__ctor(&PoolExpr, kPoolExprBlockSize, 64); FixedMemoryPool__ctor(&PoolFrame, kPoolFrameBlockSize, 128); - MemoryPool__ctor(&PoolObject); } void MemoryPools__finalize(){ FixedMemoryPool__dtor(&PoolExpr); FixedMemoryPool__dtor(&PoolFrame); - MemoryPool__dtor(&PoolObject); } void* PoolExpr_alloc() { @@ -269,57 +91,3 @@ void* PoolFrame_alloc() { void PoolFrame_dealloc(void* p) { FixedMemoryPool__dealloc(&PoolFrame, p); } - -void* PoolObject_alloc() { - return MemoryPool__alloc(&PoolObject); -} - -void PoolObject_dealloc(void* p) { - MemoryPool__dealloc(&PoolObject, p); -} - -void PoolObject_shrink_to_fit() { - MemoryPool__shrink_to_fit(&PoolObject); -} - -void Pools_debug_info(char* buffer, int size) { - double BYTES_PER_MB = 1024.0f * 1024.0f; - double BYTES_PER_KB = 1024.0f; - int n = 0; - n = snprintf( - buffer, size, "PoolExpr: %.2f KB (used) / %.2f KB (total) - %.2f KB (exceeded)\n", - FixedMemoryPool__used_bytes(&PoolExpr) / BYTES_PER_KB, - FixedMemoryPool__total_bytes(&PoolExpr) / BYTES_PER_KB, - PoolExpr.exceeded_bytes / BYTES_PER_KB - ); - buffer += n; size -= n; - n = snprintf( - buffer, size, "PoolFrame: %.2f KB (used) / %.2f KB (total) - %.2f KB (exceeded)\n", - FixedMemoryPool__used_bytes(&PoolFrame) / BYTES_PER_KB, - FixedMemoryPool__total_bytes(&PoolFrame) / BYTES_PER_KB, - PoolFrame.exceeded_bytes / BYTES_PER_KB - ); - buffer += n; size -= n; - // PoolObject - int empty_arenas = PoolObject._empty_arenas.length; - int arenas = PoolObject._arenas.length; - // print empty arenas count - n = snprintf( - buffer, size, "PoolObject: %d empty arenas, %d arenas\n", - empty_arenas, arenas - ); - buffer += n; size -= n; - // log each non-empty arena - LinkedList__apply(&PoolObject._arenas, - MemoryPoolArena* arena = (MemoryPoolArena*)node; - n = snprintf( - buffer, size, " - %p: %.2f MB (used) / %.2f MB (total)\n", - (void*)arena, - MemoryPoolArena__used_bytes(arena) / BYTES_PER_MB, - MemoryPoolArena__total_bytes(arena) / BYTES_PER_MB - ); - buffer += n; size -= n; - ); -} - -#undef LinkedList__apply \ No newline at end of file diff --git a/src/common/vector.c b/src/common/vector.c index f26a833a..32b97fe4 100644 --- a/src/common/vector.c +++ b/src/common/vector.c @@ -62,3 +62,9 @@ void* c11_vector__submit(c11_vector* self, int* length) { self->capacity = 0; return retval; } + +void c11_vector__swap(c11_vector *self, c11_vector *other){ + c11_vector tmp = *self; + *self = *other; + *other = tmp; +} diff --git a/src/interpreter/heap.c b/src/interpreter/heap.c index be74615c..952a1455 100644 --- a/src/interpreter/heap.c +++ b/src/interpreter/heap.c @@ -1,39 +1,46 @@ #include "pocketpy/interpreter/heap.h" -#include "pocketpy/common/memorypool.h" #include "pocketpy/config.h" +#include "pocketpy/interpreter/objectpool.h" #include "pocketpy/objects/base.h" +#include "pocketpy/pocketpy.h" -void ManagedHeap__ctor(ManagedHeap* self, VM* vm) { - c11_vector__ctor(&self->no_gc, sizeof(PyObject*)); - c11_vector__ctor(&self->gen, sizeof(PyObject*)); +void ManagedHeap__ctor(ManagedHeap* self) { + MultiPool__ctor(&self->small_objects); + c11_vector__ctor(&self->large_objects, 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->vm = vm; } void ManagedHeap__dtor(ManagedHeap* self) { - for(int i = 0; i < self->gen.length; i++) { - PyObject* obj = c11__getitem(PyObject*, &self->gen, i); - PyObject__delete(obj); + // 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); } - for(int i = 0; i < self->no_gc.length; i++) { - PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i); - PyObject__delete(obj); - } - c11_vector__dtor(&self->no_gc); - c11_vector__dtor(&self->gen); + c11_vector__dtor(&self->large_objects); } void ManagedHeap__collect_if_needed(ManagedHeap* self) { if(!self->gc_enabled) return; if(self->gc_counter < self->gc_threshold) return; self->gc_counter = 0; - ManagedHeap__collect(self); - self->gc_threshold = self->gen.length * 2; - if(self->gc_threshold < PK_GC_MIN_THRESHOLD) { self->gc_threshold = PK_GC_MIN_THRESHOLD; } + int freed = ManagedHeap__collect(self); + // 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; + int upper = self->gc_threshold * 2; + int lower = c11__max(PK_GC_MIN_THRESHOLD, self->gc_threshold / 2 + 1); + self->gc_threshold = c11__min(c11__max(avg_freed, lower), upper); } int ManagedHeap__collect(ManagedHeap* self) { @@ -43,71 +50,52 @@ int ManagedHeap__collect(ManagedHeap* self) { } int ManagedHeap__sweep(ManagedHeap* self) { - c11_vector alive; - c11_vector__ctor(&alive, sizeof(PyObject*)); - c11_vector__reserve(&alive, self->gen.length / 2); - - for(int i = 0; i < self->gen.length; i++) { - PyObject* obj = c11__getitem(PyObject*, &self->gen, i); + // small_objects + int small_freed = MultiPool__sweep_dealloc(&self->small_objects); + // 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_vector__push(PyObject*, &alive, obj); + c11__setitem(PyObject*, &self->large_objects, large_living_count, obj); + large_living_count++; } else { - PyObject__delete(obj); + PyObject__dtor(obj); + PK_FREE(obj); + // type and module objects are perpectual + assert(obj->type != tp_type); + assert(obj->type != tp_module); } } - - // clear _no_gc marked flag - for(int i = 0; i < self->no_gc.length; i++) { - PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i); - obj->gc_marked = false; - } - - int freed = self->gen.length - alive.length; - - // destroy old gen - c11_vector__dtor(&self->gen); - // move alive to gen - self->gen = alive; - - PoolObject_shrink_to_fit(); - return freed; -} - -PyObject* ManagedHeap__new(ManagedHeap* self, py_Type type, int slots, int udsize) { - PyObject* obj = PyObject__new(type, slots, udsize); - c11_vector__push(PyObject*, &self->no_gc, obj); - return obj; + // shrink `self->large_objects` + int large_freed = self->large_objects.length - large_living_count; + self->large_objects.length = large_living_count; + return small_freed + large_freed; } PyObject* ManagedHeap__gcnew(ManagedHeap* self, py_Type type, int slots, int udsize) { - PyObject* obj = PyObject__new(type, slots, udsize); - c11_vector__push(PyObject*, &self->gen, obj); - self->gc_counter++; - return obj; -} - -PyObject* PyObject__new(py_Type type, int slots, int size) { assert(slots >= 0 || slots == -1); - PyObject* self; + PyObject* obj; // header + slots + udsize - size = sizeof(PyObject) + PK_OBJ_SLOTS_SIZE(slots) + size; - if(!PK_LOW_MEMORY_MODE && size <= kPoolObjectBlockSize) { - self = PoolObject_alloc(); - self->gc_is_large = false; + int size = sizeof(PyObject) + PK_OBJ_SLOTS_SIZE(slots) + udsize; + if(!PK_LOW_MEMORY_MODE && size <= kPoolMaxBlockSize) { + obj = MultiPool__alloc(&self->small_objects, size); } else { - self = PK_MALLOC(size); - self->gc_is_large = true; + obj = PK_MALLOC(size); + c11_vector__push(PyObject*, &self->large_objects, obj); } - self->type = type; - self->gc_marked = false; - self->slots = slots; + obj->type = type; + obj->gc_marked = false; + obj->slots = slots; // initialize slots or dict if(slots >= 0) { - memset(self->flex, 0, slots * sizeof(py_TValue)); + memset(obj->flex, 0, slots * sizeof(py_TValue)); } else { - NameDict__ctor((void*)self->flex); + NameDict__ctor((void*)obj->flex); } - return self; -} + + self->gc_counter++; + return obj; +} \ No newline at end of file diff --git a/src/interpreter/objectpool.c b/src/interpreter/objectpool.c new file mode 100644 index 00000000..34def166 --- /dev/null +++ b/src/interpreter/objectpool.c @@ -0,0 +1,174 @@ +#include "pocketpy/interpreter/objectpool.h" + +#include "pocketpy/config.h" +#include "pocketpy/objects/object.h" + +#include +#include +#include +#include + +static PoolArena* PoolArena__new(int block_size) { + assert(kPoolArenaSize % block_size == 0); + int block_count = kPoolArenaSize / block_size; + int total_size = sizeof(PoolArena) + sizeof(int) * block_count; + PoolArena* self = PK_MALLOC(total_size); + self->block_size = block_size; + self->block_count = block_count; + self->unused_count = block_count; + self->unused = PK_MALLOC(sizeof(int) * block_count); + for(int i = 0; i < block_count; i++) { + self->unused[i] = i; + } + memset(self->data, 0, kPoolArenaSize); + return self; +} + +static void PoolArena__delete(PoolArena* self) { + for(int i = 0; i < self->block_count; i++) { + PyObject* obj = (PyObject*)(self->data + i * self->block_size); + if(obj->type != 0) PyObject__dtor(obj); + } + PK_FREE(self->unused); + PK_FREE(self); +} + +static void* PoolArena__alloc(PoolArena* self) { + assert(self->unused_count > 0); + int index = self->unused[self->unused_count - 1]; + self->unused_count--; + return self->data + index * self->block_size; +} + +static int PoolArena__sweep_dealloc(PoolArena* self) { + int freed = 0; + self->unused_count = 0; + for(int i = 0; i < self->block_count; i++) { + PyObject* obj = (PyObject*)(self->data + i * self->block_size); + if(obj->type == 0) { + self->unused[self->unused_count] = i; + self->unused_count++; + } else { + if(!obj->gc_marked) { + obj->type = 0; + freed++; + self->unused[self->unused_count] = i; + self->unused_count++; + } else { + obj->gc_marked = false; + } + } + } + return freed; +} + +static void Pool__ctor(Pool* self, int block_size) { + c11_vector__ctor(&self->arenas, sizeof(PoolArena*)); + c11_vector__ctor(&self->not_free_arenas, sizeof(PoolArena*)); + self->block_size = block_size; +} + +static void Pool__dtor(Pool* self) { + c11__foreach(PoolArena*, &self->arenas, arena) { PoolArena__delete(*arena); } + c11__foreach(PoolArena*, &self->not_free_arenas, arena) { PoolArena__delete(*arena); } + c11_vector__dtor(&self->arenas); + c11_vector__dtor(&self->not_free_arenas); +} + +static void* Pool__alloc(Pool* self) { + PoolArena* arena; + if(self->arenas.length == 0) { + arena = PoolArena__new(self->block_size); + c11_vector__push(PoolArena*, &self->arenas, arena); + } else { + arena = c11_vector__back(PoolArena*, &self->arenas); + } + void* ptr = PoolArena__alloc(arena); + if(arena->unused_count == 0) { + c11_vector__pop(&self->arenas); + c11_vector__push(PoolArena*, &self->not_free_arenas, arena); + } + return ptr; +} + +static int Pool__sweep_dealloc(Pool* self, c11_vector* arenas, c11_vector* not_free_arenas) { + c11_vector__clear(arenas); + c11_vector__clear(not_free_arenas); + + int freed = 0; + for(int i = 0; i < self->arenas.length; i++) { + PoolArena* item = c11__getitem(PoolArena*, &self->arenas, i); + assert(item->unused_count > 0); + freed += PoolArena__sweep_dealloc(item); + if(item->unused_count == item->block_count) { + // all free + if(self->arenas.length > 0) { + // at least one arena + PoolArena__delete(item); + } else { + // no arena + c11_vector__push(PoolArena*, arenas, item); + } + } else { + // some free + c11_vector__push(PoolArena*, arenas, item); + } + } + for(int i = 0; i < self->not_free_arenas.length; i++) { + PoolArena* item = c11__getitem(PoolArena*, &self->not_free_arenas, i); + freed += PoolArena__sweep_dealloc(item); + if(item->unused_count == 0) { + // still not free + c11_vector__push(PoolArena*, not_free_arenas, item); + } else { + if(item->unused_count == item->block_count) { + // all free + PoolArena__delete(item); + } else { + // some free + c11_vector__push(PoolArena*, arenas, item); + } + } + } + + c11_vector__swap(&self->arenas, arenas); + c11_vector__swap(&self->not_free_arenas, not_free_arenas); + return freed; +} + +void* MultiPool__alloc(MultiPool* self, int size) { + if(size == 0) return NULL; + int index = (size - 1) >> 5; + if(index < kMultiPoolCount) { + Pool* pool = &self->pools[index]; + return Pool__alloc(pool); + } + return NULL; +} + +int MultiPool__sweep_dealloc(MultiPool* self) { + c11_vector arenas; + c11_vector not_free_arenas; + c11_vector__ctor(&arenas, sizeof(PoolArena*)); + c11_vector__ctor(¬_free_arenas, sizeof(PoolArena*)); + int freed = 0; + for(int i = 0; i < kMultiPoolCount; i++) { + Pool* item = &self->pools[i]; + freed += Pool__sweep_dealloc(item, &arenas, ¬_free_arenas); + } + c11_vector__dtor(&arenas); + c11_vector__dtor(¬_free_arenas); + return freed; +} + +void MultiPool__ctor(MultiPool* self) { + for(int i = 0; i < kMultiPoolCount; i++) { + Pool__ctor(&self->pools[i], 32 * (i + 1)); + } +} + +void MultiPool__dtor(MultiPool* self) { + for(int i = 0; i < kMultiPoolCount; i++) { + Pool__dtor(&self->pools[i]); + } +} \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 5d5deb6d..1d77862e 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -1,5 +1,4 @@ #include "pocketpy/interpreter/vm.h" -#include "pocketpy/common/memorypool.h" #include "pocketpy/common/sstream.h" #include "pocketpy/common/utils.h" #include "pocketpy/interpreter/generator.h" @@ -76,7 +75,7 @@ void VM__ctor(VM* self) { self->__curr_class = NULL; self->__curr_function = NULL; - ManagedHeap__ctor(&self->heap, self); + ManagedHeap__ctor(&self->heap); ValueStack__ctor(&self->stack); /* Init Builtin Types */ @@ -569,15 +568,10 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall } /****************************************/ -void PyObject__delete(PyObject* self) { +void PyObject__dtor(PyObject* self) { py_TypeInfo* ti = pk__type_info(self->type); if(ti->dtor) ti->dtor(PyObject__userdata(self)); if(self->slots == -1) NameDict__dtor(PyObject__dict(self)); - if(self->gc_is_large) { - PK_FREE(self); - } else { - PoolObject_dealloc(self); - } } static void mark_object(PyObject* obj); @@ -636,10 +630,10 @@ void CodeObject__gc_mark(const CodeObject* self) { } void ManagedHeap__mark(ManagedHeap* self) { - VM* vm = self->vm; - // mark heap objects - for(int i = 0; i < self->no_gc.length; i++) { - PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i); + VM* vm = pk_current_vm; + // mark large objects + for(int i = 0; i < self->large_objects.length; i++) { + PyObject* obj = c11__getitem(PyObject*, &self->large_objects, i); mark_object(obj); } // mark value stack