mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
up
This commit is contained in:
parent
97d0b17b8e
commit
6a27bc8bda
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,3 +23,4 @@ plugins/godot/godot-cpp/
|
|||||||
src/_generated.h
|
src/_generated.h
|
||||||
profile.sh
|
profile.sh
|
||||||
test
|
test
|
||||||
|
tmp.rar
|
||||||
|
@ -16,7 +16,7 @@ __NEXT_STEP:;
|
|||||||
* `Args` containing strong references is safe if it is passed to `call` or `fast_call`
|
* `Args` containing strong references is safe if it is passed to `call` or `fast_call`
|
||||||
*/
|
*/
|
||||||
#if !DEBUG_NO_AUTO_GC
|
#if !DEBUG_NO_AUTO_GC
|
||||||
heap._auto_collect(this);
|
heap._auto_collect();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const Bytecode& byte = frame->next_bytecode();
|
const Bytecode& byte = frame->next_bytecode();
|
||||||
|
@ -36,8 +36,9 @@
|
|||||||
#define DEBUG_DIS_EXEC 0
|
#define DEBUG_DIS_EXEC 0
|
||||||
#define DEBUG_DIS_EXEC_MIN 1
|
#define DEBUG_DIS_EXEC_MIN 1
|
||||||
#define DEBUG_CEVAL_STEP 0
|
#define DEBUG_CEVAL_STEP 0
|
||||||
#define DEBUG_FULL_EXCEPTION 0
|
#define DEBUG_FULL_EXCEPTION 1
|
||||||
#define DEBUG_NO_AUTO_GC 1
|
#define DEBUG_MEMORY_POOL 0
|
||||||
|
#define DEBUG_NO_AUTO_GC 0
|
||||||
#define DEBUG_GC_STATS 0
|
#define DEBUG_GC_STATS 0
|
||||||
|
|
||||||
#if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__)
|
#if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__)
|
||||||
|
16
src/gc.h
16
src/gc.h
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "obj.h"
|
#include "obj.h"
|
||||||
#include "codeobject.h"
|
#include "codeobject.h"
|
||||||
#include "namedict.h"
|
#include "namedict.h"
|
||||||
@ -10,6 +11,7 @@ struct ManagedHeap{
|
|||||||
std::vector<PyObject*> _no_gc;
|
std::vector<PyObject*> _no_gc;
|
||||||
std::vector<PyObject*> gen;
|
std::vector<PyObject*> gen;
|
||||||
VM* vm;
|
VM* vm;
|
||||||
|
MemoryPool<> pool;
|
||||||
|
|
||||||
ManagedHeap(VM* vm): vm(vm) {}
|
ManagedHeap(VM* vm): vm(vm) {}
|
||||||
|
|
||||||
@ -36,7 +38,8 @@ struct ManagedHeap{
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
PyObject* gcnew(Type type, T&& val){
|
PyObject* gcnew(Type type, T&& val){
|
||||||
PyObject* obj = new Py_<std::decay_t<T>>(type, std::forward<T>(val));
|
using __T = Py_<std::decay_t<T>>;
|
||||||
|
PyObject* obj = new(pool.alloc<__T>()) __T(type, std::forward<T>(val));
|
||||||
gen.push_back(obj);
|
gen.push_back(obj);
|
||||||
gc_counter++;
|
gc_counter++;
|
||||||
return obj;
|
return obj;
|
||||||
@ -44,16 +47,19 @@ struct ManagedHeap{
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
PyObject* _new(Type type, T&& val){
|
PyObject* _new(Type type, T&& val){
|
||||||
PyObject* obj = new Py_<std::decay_t<T>>(type, std::forward<T>(val));
|
using __T = Py_<std::decay_t<T>>;
|
||||||
|
PyObject* obj = new(pool.alloc<__T>()) __T(type, std::forward<T>(val));
|
||||||
obj->gc.enabled = false;
|
obj->gc.enabled = false;
|
||||||
_no_gc.push_back(obj);
|
_no_gc.push_back(obj);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG_GC_STATS
|
||||||
inline static std::map<Type, int> deleted;
|
inline static std::map<Type, int> deleted;
|
||||||
|
#endif
|
||||||
|
|
||||||
~ManagedHeap(){
|
~ManagedHeap(){
|
||||||
for(PyObject* obj: _no_gc) delete obj;
|
for(PyObject* obj: _no_gc) obj->~PyObject(), pool.dealloc(obj);
|
||||||
#if DEBUG_GC_STATS
|
#if DEBUG_GC_STATS
|
||||||
for(auto& [type, count]: deleted){
|
for(auto& [type, count]: deleted){
|
||||||
std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
|
std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
|
||||||
@ -68,8 +74,10 @@ struct ManagedHeap{
|
|||||||
obj->gc.marked = false;
|
obj->gc.marked = false;
|
||||||
alive.push_back(obj);
|
alive.push_back(obj);
|
||||||
}else{
|
}else{
|
||||||
|
#if DEBUG_GC_STATS
|
||||||
deleted[obj->type] += 1;
|
deleted[obj->type] += 1;
|
||||||
delete obj;
|
#endif
|
||||||
|
obj->~PyObject(), pool.dealloc(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
223
src/memory.h
223
src/memory.h
@ -105,4 +105,227 @@ struct FreeListA {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct LinkedListNode{
|
||||||
|
LinkedListNode* prev;
|
||||||
|
LinkedListNode* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct DoubleLinkedList{
|
||||||
|
static_assert(std::is_base_of_v<LinkedListNode, T>);
|
||||||
|
int _size;
|
||||||
|
LinkedListNode head;
|
||||||
|
LinkedListNode tail;
|
||||||
|
|
||||||
|
DoubleLinkedList(): _size(0){
|
||||||
|
head.prev = nullptr;
|
||||||
|
head.next = &tail;
|
||||||
|
tail.prev = &head;
|
||||||
|
tail.next = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(T* node){
|
||||||
|
node->prev = tail.prev;
|
||||||
|
node->next = &tail;
|
||||||
|
tail.prev->next = node;
|
||||||
|
tail.prev = node;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_front(T* node){
|
||||||
|
node->prev = &head;
|
||||||
|
node->next = head.next;
|
||||||
|
head.next->prev = node;
|
||||||
|
head.next = node;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back(){
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(empty()) throw std::runtime_error("DoubleLinkedList::pop_back() called on empty list");
|
||||||
|
#endif
|
||||||
|
tail.prev->prev->next = &tail;
|
||||||
|
tail.prev = tail.prev->prev;
|
||||||
|
_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_front(){
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(empty()) throw std::runtime_error("DoubleLinkedList::pop_front() called on empty list");
|
||||||
|
#endif
|
||||||
|
head.next->next->prev = &head;
|
||||||
|
head.next = head.next->next;
|
||||||
|
_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* back() const {
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(empty()) throw std::runtime_error("DoubleLinkedList::back() called on empty list");
|
||||||
|
#endif
|
||||||
|
return static_cast<T*>(tail.prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* front() const {
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(empty()) throw std::runtime_error("DoubleLinkedList::front() called on empty list");
|
||||||
|
#endif
|
||||||
|
return static_cast<T*>(head.next);
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(T* node){
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(empty()) throw std::runtime_error("DoubleLinkedList::erase() called on empty list");
|
||||||
|
LinkedListNode* n = head.next;
|
||||||
|
while(n != &tail){
|
||||||
|
if(n == node) break;
|
||||||
|
n = n->next;
|
||||||
|
}
|
||||||
|
if(n != node) throw std::runtime_error("DoubleLinkedList::erase() called on node not in the list");
|
||||||
|
#endif
|
||||||
|
node->prev->next = node->next;
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_all_back(DoubleLinkedList<T>& other){
|
||||||
|
if(other.empty()) return;
|
||||||
|
other.tail.prev->next = &tail;
|
||||||
|
tail.prev->next = other.head.next;
|
||||||
|
other.head.next->prev = tail.prev;
|
||||||
|
tail.prev = other.tail.prev;
|
||||||
|
_size += other._size;
|
||||||
|
other.head.next = &other.tail;
|
||||||
|
other.tail.prev = &other.head;
|
||||||
|
other._size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(size() == 0){
|
||||||
|
if(head.next != &tail || tail.prev != &head){
|
||||||
|
throw std::runtime_error("DoubleLinkedList::size() returned 0 but the list is not empty");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return _size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const { return _size; }
|
||||||
|
|
||||||
|
void apply(std::function<void(T*)> func){
|
||||||
|
LinkedListNode* p = head.next;
|
||||||
|
while(p != &tail){
|
||||||
|
LinkedListNode* next = p->next;
|
||||||
|
func(static_cast<T*>(p));
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int __BlockSize=128>
|
||||||
|
struct MemoryPool{
|
||||||
|
static const size_t __MaxBlocks = 256*1024 / __BlockSize;
|
||||||
|
struct Block{
|
||||||
|
void* arena;
|
||||||
|
char data[__BlockSize];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Arena: LinkedListNode{
|
||||||
|
Block _blocks[__MaxBlocks];
|
||||||
|
Block* _free_list[__MaxBlocks];
|
||||||
|
int _free_list_size;
|
||||||
|
|
||||||
|
Arena(): _free_list_size(__MaxBlocks) {
|
||||||
|
for(int i=0; i<__MaxBlocks; i++){
|
||||||
|
_blocks[i].arena = this;
|
||||||
|
_free_list[i] = &_blocks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const { return _free_list_size == 0; }
|
||||||
|
bool full() const { return _free_list_size == __MaxBlocks; }
|
||||||
|
|
||||||
|
Block* alloc(){
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(empty()) throw std::runtime_error("Arena::alloc() called on empty arena");
|
||||||
|
#endif
|
||||||
|
_free_list_size--;
|
||||||
|
return _free_list[_free_list_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
void dealloc(Block* block){
|
||||||
|
#if DEBUG_MEMORY_POOL
|
||||||
|
if(full()) throw std::runtime_error("Arena::dealloc() called on full arena");
|
||||||
|
#endif
|
||||||
|
_free_list[_free_list_size] = block;
|
||||||
|
_free_list_size++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DoubleLinkedList<Arena> _arenas;
|
||||||
|
DoubleLinkedList<Arena> _empty_arenas;
|
||||||
|
DoubleLinkedList<Arena> _full_arenas;
|
||||||
|
|
||||||
|
template<typename __T>
|
||||||
|
void* alloc() { return alloc(sizeof(__T)); }
|
||||||
|
|
||||||
|
void* alloc(size_t size){
|
||||||
|
if(size > __BlockSize){
|
||||||
|
void* p = malloc(sizeof(void*) + size);
|
||||||
|
memset(p, 0, sizeof(void*));
|
||||||
|
return (char*)p + sizeof(void*);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_arenas.empty()){
|
||||||
|
if(_full_arenas.empty()){
|
||||||
|
_arenas.push_back(new Arena());
|
||||||
|
}else{
|
||||||
|
_arenas.move_all_back(_full_arenas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Arena* arena = _arenas.back();
|
||||||
|
void* p = arena->alloc()->data;
|
||||||
|
if(arena->empty()){
|
||||||
|
_arenas.pop_back();
|
||||||
|
_empty_arenas.push_back(arena);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dealloc(void* p){
|
||||||
|
Block* block = (Block*)((char*)p - sizeof(void*));
|
||||||
|
if(block->arena == nullptr){
|
||||||
|
free(block);
|
||||||
|
}else{
|
||||||
|
Arena* arena = (Arena*)block->arena;
|
||||||
|
if(arena->empty()){
|
||||||
|
_empty_arenas.erase(arena);
|
||||||
|
_arenas.push_front(arena);
|
||||||
|
arena->dealloc(block);
|
||||||
|
}else{
|
||||||
|
arena->dealloc(block);
|
||||||
|
if(arena->full()){ // && _arenas.size() > 2
|
||||||
|
_arenas.erase(arena);
|
||||||
|
if(_full_arenas.size() < 8){
|
||||||
|
_full_arenas.push_back(arena);
|
||||||
|
}else{
|
||||||
|
delete arena;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~MemoryPool(){
|
||||||
|
// std::cout << _arenas.size() << std::endl;
|
||||||
|
// std::cout << _empty_arenas.size() << std::endl;
|
||||||
|
// std::cout << _full_arenas.size() << std::endl;
|
||||||
|
_arenas.apply([](Arena* arena){ delete arena; });
|
||||||
|
_empty_arenas.apply([](Arena* arena){ delete arena; });
|
||||||
|
_full_arenas.apply([](Arena* arena){ delete arena; });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}; // namespace pkpy
|
}; // namespace pkpy
|
||||||
|
Loading…
x
Reference in New Issue
Block a user