mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
...
This commit is contained in:
parent
162c597093
commit
9e9cbcdffd
@ -41,7 +41,7 @@ struct ManagedHeap{
|
|||||||
PyObject* gcnew(Type type, Args&&... args){
|
PyObject* gcnew(Type type, Args&&... args){
|
||||||
using __T = Py_<std::decay_t<T>>;
|
using __T = Py_<std::decay_t<T>>;
|
||||||
// https://github.com/blueloveTH/pocketpy/issues/94#issuecomment-1594784476
|
// https://github.com/blueloveTH/pocketpy/issues/94#issuecomment-1594784476
|
||||||
PyObject* obj = new(pool64.alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<Args>(args)...);
|
PyObject* obj = new(pool64_alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<Args>(args)...);
|
||||||
gen.push_back(obj);
|
gen.push_back(obj);
|
||||||
gc_counter++;
|
gc_counter++;
|
||||||
return obj;
|
return obj;
|
||||||
@ -51,7 +51,7 @@ struct ManagedHeap{
|
|||||||
PyObject* _new(Type type, Args&&... args){
|
PyObject* _new(Type type, Args&&... args){
|
||||||
using __T = Py_<std::decay_t<T>>;
|
using __T = Py_<std::decay_t<T>>;
|
||||||
// https://github.com/blueloveTH/pocketpy/issues/94#issuecomment-1594784476
|
// https://github.com/blueloveTH/pocketpy/issues/94#issuecomment-1594784476
|
||||||
PyObject* obj = new(pool64.alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<Args>(args)...);
|
PyObject* obj = new(pool64_alloc<__T>()) Py_<std::decay_t<T>>(type, std::forward<Args>(args)...);
|
||||||
obj->gc.enabled = false;
|
obj->gc.enabled = false;
|
||||||
_no_gc.push_back(obj);
|
_no_gc.push_back(obj);
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -4,249 +4,20 @@
|
|||||||
|
|
||||||
namespace pkpy{
|
namespace pkpy{
|
||||||
|
|
||||||
struct LinkedListNode{
|
void* pool64_alloc(size_t);
|
||||||
LinkedListNode* prev;
|
void pool64_dealloc(void*);
|
||||||
LinkedListNode* next;
|
|
||||||
};
|
void* pool128_alloc(size_t);
|
||||||
|
void pool128_dealloc(void*);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct DoubleLinkedList{
|
void* pool64_alloc(){
|
||||||
static_assert(std::is_base_of_v<LinkedListNode, T>);
|
return pool64_alloc(sizeof(T));
|
||||||
int _size;
|
}
|
||||||
LinkedListNode head;
|
|
||||||
LinkedListNode tail;
|
|
||||||
|
|
||||||
DoubleLinkedList(): _size(0){
|
template<typename T>
|
||||||
head.prev = nullptr;
|
void* pool128_alloc(){
|
||||||
head.next = &tail;
|
return pool128_alloc(sizeof(T));
|
||||||
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 PK_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 PK_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 PK_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 PK_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 PK_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 PK_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; }
|
|
||||||
|
|
||||||
template<typename Func>
|
|
||||||
void apply(Func 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;
|
|
||||||
bool dirty;
|
|
||||||
|
|
||||||
Arena(): _free_list_size(__MaxBlocks), dirty(false){
|
|
||||||
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; }
|
|
||||||
|
|
||||||
size_t allocated_size() const{
|
|
||||||
return __BlockSize * (__MaxBlocks - _free_list_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Block* alloc(){
|
|
||||||
#if PK_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 PK_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++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MemoryPool() = default;
|
|
||||||
MemoryPool(const MemoryPool&) = delete;
|
|
||||||
MemoryPool& operator=(const MemoryPool&) = delete;
|
|
||||||
MemoryPool(MemoryPool&&) = delete;
|
|
||||||
MemoryPool& operator=(MemoryPool&&) = delete;
|
|
||||||
|
|
||||||
DoubleLinkedList<Arena> _arenas;
|
|
||||||
DoubleLinkedList<Arena> _empty_arenas;
|
|
||||||
|
|
||||||
template<typename __T>
|
|
||||||
void* alloc() { return alloc(sizeof(__T)); }
|
|
||||||
|
|
||||||
void* alloc(size_t size){
|
|
||||||
PK_GLOBAL_SCOPE_LOCK();
|
|
||||||
#if PK_DEBUG_NO_MEMORY_POOL
|
|
||||||
return malloc(size);
|
|
||||||
#endif
|
|
||||||
if(size > __BlockSize){
|
|
||||||
void* p = malloc(sizeof(void*) + size);
|
|
||||||
memset(p, 0, sizeof(void*));
|
|
||||||
return (char*)p + sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_arenas.empty()){
|
|
||||||
// std::cout << _arenas.size() << ',' << _empty_arenas.size() << ',' << _full_arenas.size() << std::endl;
|
|
||||||
_arenas.push_back(new Arena());
|
|
||||||
}
|
|
||||||
Arena* arena = _arenas.back();
|
|
||||||
void* p = arena->alloc()->data;
|
|
||||||
if(arena->empty()){
|
|
||||||
_arenas.pop_back();
|
|
||||||
arena->dirty = true;
|
|
||||||
_empty_arenas.push_back(arena);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dealloc(void* p){
|
|
||||||
PK_GLOBAL_SCOPE_LOCK();
|
|
||||||
#if PK_DEBUG_NO_MEMORY_POOL
|
|
||||||
free(p);
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
#if PK_DEBUG_MEMORY_POOL
|
|
||||||
if(p == nullptr) throw std::runtime_error("MemoryPool::dealloc() called on nullptr");
|
|
||||||
#endif
|
|
||||||
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() && arena->dirty){
|
|
||||||
_arenas.erase(arena);
|
|
||||||
delete arena;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t allocated_size() {
|
|
||||||
size_t n = 0;
|
|
||||||
_arenas.apply([&n](Arena* arena){ n += arena->allocated_size(); });
|
|
||||||
_empty_arenas.apply([&n](Arena* arena){ n += arena->allocated_size(); });
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
~MemoryPool(){
|
|
||||||
_arenas.apply([](Arena* arena){ delete arena; });
|
|
||||||
_empty_arenas.apply([](Arena* arena){ delete arena; });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline MemoryPool<64> pool64;
|
|
||||||
inline MemoryPool<128> pool128;
|
|
||||||
|
|
||||||
}; // namespace pkpy
|
}; // namespace pkpy
|
||||||
|
@ -38,7 +38,7 @@ while(!_items[i].first.empty()) { \
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define NAMEDICT_ALLOC() \
|
#define NAMEDICT_ALLOC() \
|
||||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item)); \
|
_items = (Item*)pool128_alloc(_capacity * sizeof(Item)); \
|
||||||
memset(_items, 0, _capacity * sizeof(Item)); \
|
memset(_items, 0, _capacity * sizeof(Item)); \
|
||||||
|
|
||||||
NameDictImpl(float load_factor=0.67f):
|
NameDictImpl(float load_factor=0.67f):
|
||||||
@ -54,14 +54,14 @@ while(!_items[i].first.empty()) { \
|
|||||||
}
|
}
|
||||||
|
|
||||||
NameDictImpl& operator=(const NameDictImpl& other) {
|
NameDictImpl& operator=(const NameDictImpl& other) {
|
||||||
pool128.dealloc(_items);
|
pool128_dealloc(_items);
|
||||||
memcpy(this, &other, sizeof(NameDictImpl));
|
memcpy(this, &other, sizeof(NameDictImpl));
|
||||||
NAMEDICT_ALLOC()
|
NAMEDICT_ALLOC()
|
||||||
for(int i=0; i<_capacity; i++) _items[i] = other._items[i];
|
for(int i=0; i<_capacity; i++) _items[i] = other._items[i];
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
~NameDictImpl(){ pool128.dealloc(_items); }
|
~NameDictImpl(){ pool128_dealloc(_items); }
|
||||||
|
|
||||||
NameDictImpl(NameDictImpl&&) = delete;
|
NameDictImpl(NameDictImpl&&) = delete;
|
||||||
NameDictImpl& operator=(NameDictImpl&&) = delete;
|
NameDictImpl& operator=(NameDictImpl&&) = delete;
|
||||||
@ -103,7 +103,7 @@ while(!_items[i].first.empty()) { \
|
|||||||
if(ok) FATAL_ERROR();
|
if(ok) FATAL_ERROR();
|
||||||
_items[j] = old_items[i];
|
_items[j] = old_items[i];
|
||||||
}
|
}
|
||||||
pool128.dealloc(old_items);
|
pool128_dealloc(old_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _try_perfect_rehash(){
|
void _try_perfect_rehash(){
|
||||||
|
@ -119,7 +119,7 @@ struct PyObject{
|
|||||||
virtual ~PyObject();
|
virtual ~PyObject();
|
||||||
|
|
||||||
void enable_instance_dict(float lf=kInstAttrLoadFactor) {
|
void enable_instance_dict(float lf=kInstAttrLoadFactor) {
|
||||||
_attr = new(pool64.alloc<NameDict>()) NameDict(lf);
|
_attr = new(pool64_alloc<NameDict>()) NameDict(lf);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,15 +16,15 @@ struct pod_vector{
|
|||||||
T* _data;
|
T* _data;
|
||||||
|
|
||||||
pod_vector(): _size(0), _capacity(N) {
|
pod_vector(): _size(0), _capacity(N) {
|
||||||
_data = (T*)pool64.alloc(_capacity * sizeof(T));
|
_data = (T*)pool64_alloc(_capacity * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
pod_vector(int size): _size(size), _capacity(std::max(N, size)) {
|
pod_vector(int size): _size(size), _capacity(std::max(N, size)) {
|
||||||
_data = (T*)pool64.alloc(_capacity * sizeof(T));
|
_data = (T*)pool64_alloc(_capacity * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
pod_vector(const pod_vector& other): _size(other._size), _capacity(other._capacity) {
|
pod_vector(const pod_vector& other): _size(other._size), _capacity(other._capacity) {
|
||||||
_data = (T*)pool64.alloc(_capacity * sizeof(T));
|
_data = (T*)pool64_alloc(_capacity * sizeof(T));
|
||||||
memcpy(_data, other._data, sizeof(T) * _size);
|
memcpy(_data, other._data, sizeof(T) * _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ struct pod_vector{
|
|||||||
}
|
}
|
||||||
|
|
||||||
pod_vector& operator=(pod_vector&& other) noexcept {
|
pod_vector& operator=(pod_vector&& other) noexcept {
|
||||||
if(_data!=nullptr) pool64.dealloc(_data);
|
if(_data!=nullptr) pool64_dealloc(_data);
|
||||||
_size = other._size;
|
_size = other._size;
|
||||||
_capacity = other._capacity;
|
_capacity = other._capacity;
|
||||||
_data = other._data;
|
_data = other._data;
|
||||||
@ -63,10 +63,10 @@ struct pod_vector{
|
|||||||
if(cap <= _capacity) return;
|
if(cap <= _capacity) return;
|
||||||
_capacity = cap;
|
_capacity = cap;
|
||||||
T* old_data = _data;
|
T* old_data = _data;
|
||||||
_data = (T*)pool64.alloc(_capacity * sizeof(T));
|
_data = (T*)pool64_alloc(_capacity * sizeof(T));
|
||||||
if(old_data!=nullptr){
|
if(old_data!=nullptr){
|
||||||
memcpy(_data, old_data, sizeof(T) * _size);
|
memcpy(_data, old_data, sizeof(T) * _size);
|
||||||
pool64.dealloc(old_data);
|
pool64_dealloc(old_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ struct pod_vector{
|
|||||||
}
|
}
|
||||||
|
|
||||||
~pod_vector() {
|
~pod_vector() {
|
||||||
if(_data!=nullptr) pool64.dealloc(_data);
|
if(_data!=nullptr) pool64_dealloc(_data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
20
src/dict.cpp
20
src/dict.cpp
@ -5,9 +5,9 @@ namespace pkpy{
|
|||||||
Dict::Dict(VM* vm): vm(vm), _capacity(__Capacity),
|
Dict::Dict(VM* vm): vm(vm), _capacity(__Capacity),
|
||||||
_mask(__Capacity-1),
|
_mask(__Capacity-1),
|
||||||
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
|
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
|
||||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
_items = (Item*)pool128_alloc(_capacity * sizeof(Item));
|
||||||
memset(_items, 0, _capacity * sizeof(Item));
|
memset(_items, 0, _capacity * sizeof(Item));
|
||||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
_nodes = (ItemNode*)pool64_alloc(_capacity * sizeof(ItemNode));
|
||||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,9 +33,9 @@ namespace pkpy{
|
|||||||
_critical_size = other._critical_size;
|
_critical_size = other._critical_size;
|
||||||
_head_idx = other._head_idx;
|
_head_idx = other._head_idx;
|
||||||
_tail_idx = other._tail_idx;
|
_tail_idx = other._tail_idx;
|
||||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
_items = (Item*)pool128_alloc(_capacity * sizeof(Item));
|
||||||
memcpy(_items, other._items, _capacity * sizeof(Item));
|
memcpy(_items, other._items, _capacity * sizeof(Item));
|
||||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
_nodes = (ItemNode*)pool64_alloc(_capacity * sizeof(ItemNode));
|
||||||
memcpy(_nodes, other._nodes, _capacity * sizeof(ItemNode));
|
memcpy(_nodes, other._nodes, _capacity * sizeof(ItemNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,9 +73,9 @@ namespace pkpy{
|
|||||||
_head_idx = -1;
|
_head_idx = -1;
|
||||||
_tail_idx = -1;
|
_tail_idx = -1;
|
||||||
|
|
||||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
_items = (Item*)pool128_alloc(_capacity * sizeof(Item));
|
||||||
memset(_items, 0, _capacity * sizeof(Item));
|
memset(_items, 0, _capacity * sizeof(Item));
|
||||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
_nodes = (ItemNode*)pool64_alloc(_capacity * sizeof(ItemNode));
|
||||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||||
|
|
||||||
// copy old items to new dict
|
// copy old items to new dict
|
||||||
@ -84,8 +84,8 @@ namespace pkpy{
|
|||||||
set(old_items[i].first, old_items[i].second);
|
set(old_items[i].first, old_items[i].second);
|
||||||
i = old_nodes[i].next;
|
i = old_nodes[i].next;
|
||||||
}
|
}
|
||||||
pool128.dealloc(old_items);
|
pool128_dealloc(old_items);
|
||||||
pool64.dealloc(old_nodes);
|
pool64_dealloc(old_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -167,8 +167,8 @@ namespace pkpy{
|
|||||||
|
|
||||||
Dict::~Dict(){
|
Dict::~Dict(){
|
||||||
if(_items==nullptr) return;
|
if(_items==nullptr) return;
|
||||||
pool128.dealloc(_items);
|
pool128_dealloc(_items);
|
||||||
pool64.dealloc(_nodes);
|
pool64_dealloc(_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dict::_gc_mark() const{
|
void Dict::_gc_mark() const{
|
||||||
|
@ -14,7 +14,7 @@ namespace pkpy{
|
|||||||
#endif
|
#endif
|
||||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
||||||
obj->~PyObject();
|
obj->~PyObject();
|
||||||
pool64.dealloc(obj);
|
pool64_dealloc(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ namespace pkpy{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ManagedHeap::~ManagedHeap(){
|
ManagedHeap::~ManagedHeap(){
|
||||||
for(PyObject* obj: _no_gc) { obj->~PyObject(); pool64.dealloc(obj); }
|
for(PyObject* obj: _no_gc) { obj->~PyObject(); pool64_dealloc(obj); }
|
||||||
for(PyObject* obj: gen) { obj->~PyObject(); pool64.dealloc(obj); }
|
for(PyObject* obj: gen) { obj->~PyObject(); pool64_dealloc(obj); }
|
||||||
#if PK_DEBUG_GC_STATS
|
#if PK_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;
|
||||||
|
253
src/memory.cpp
Normal file
253
src/memory.cpp
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
#include "pocketpy/memory.h"
|
||||||
|
|
||||||
|
namespace pkpy{
|
||||||
|
|
||||||
|
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 PK_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 PK_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 PK_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 PK_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 PK_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 PK_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; }
|
||||||
|
|
||||||
|
template<typename Func>
|
||||||
|
void apply(Func 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;
|
||||||
|
bool dirty;
|
||||||
|
|
||||||
|
Arena(): _free_list_size(__MaxBlocks), dirty(false){
|
||||||
|
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; }
|
||||||
|
|
||||||
|
size_t allocated_size() const{
|
||||||
|
return __BlockSize * (__MaxBlocks - _free_list_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block* alloc(){
|
||||||
|
#if PK_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 PK_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++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MemoryPool() = default;
|
||||||
|
MemoryPool(const MemoryPool&) = delete;
|
||||||
|
MemoryPool& operator=(const MemoryPool&) = delete;
|
||||||
|
MemoryPool(MemoryPool&&) = delete;
|
||||||
|
MemoryPool& operator=(MemoryPool&&) = delete;
|
||||||
|
|
||||||
|
DoubleLinkedList<Arena> _arenas;
|
||||||
|
DoubleLinkedList<Arena> _empty_arenas;
|
||||||
|
|
||||||
|
void* alloc(size_t size){
|
||||||
|
PK_GLOBAL_SCOPE_LOCK();
|
||||||
|
#if PK_DEBUG_NO_MEMORY_POOL
|
||||||
|
return malloc(size);
|
||||||
|
#endif
|
||||||
|
if(size > __BlockSize){
|
||||||
|
void* p = malloc(sizeof(void*) + size);
|
||||||
|
memset(p, 0, sizeof(void*));
|
||||||
|
return (char*)p + sizeof(void*);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_arenas.empty()){
|
||||||
|
// std::cout << _arenas.size() << ',' << _empty_arenas.size() << ',' << _full_arenas.size() << std::endl;
|
||||||
|
_arenas.push_back(new Arena());
|
||||||
|
}
|
||||||
|
Arena* arena = _arenas.back();
|
||||||
|
void* p = arena->alloc()->data;
|
||||||
|
if(arena->empty()){
|
||||||
|
_arenas.pop_back();
|
||||||
|
arena->dirty = true;
|
||||||
|
_empty_arenas.push_back(arena);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dealloc(void* p){
|
||||||
|
PK_GLOBAL_SCOPE_LOCK();
|
||||||
|
#if PK_DEBUG_NO_MEMORY_POOL
|
||||||
|
free(p);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
#if PK_DEBUG_MEMORY_POOL
|
||||||
|
if(p == nullptr) throw std::runtime_error("MemoryPool::dealloc() called on nullptr");
|
||||||
|
#endif
|
||||||
|
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() && arena->dirty){
|
||||||
|
_arenas.erase(arena);
|
||||||
|
delete arena;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// size_t allocated_size() {
|
||||||
|
// size_t n = 0;
|
||||||
|
// _arenas.apply([&n](Arena* arena){ n += arena->allocated_size(); });
|
||||||
|
// _empty_arenas.apply([&n](Arena* arena){ n += arena->allocated_size(); });
|
||||||
|
// return n;
|
||||||
|
// }
|
||||||
|
|
||||||
|
~MemoryPool(){
|
||||||
|
_arenas.apply([](Arena* arena){ delete arena; });
|
||||||
|
_empty_arenas.apply([](Arena* arena){ delete arena; });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static MemoryPool<64> pool64;
|
||||||
|
static MemoryPool<128> pool128;
|
||||||
|
|
||||||
|
void* pool64_alloc(size_t size){ return pool64.alloc(size); }
|
||||||
|
void pool64_dealloc(void* p){ pool64.dealloc(p); }
|
||||||
|
|
||||||
|
void* pool128_alloc(size_t size){ return pool128.alloc(size); }
|
||||||
|
void pool128_dealloc(void* p){ pool128.dealloc(p); }
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,6 @@ namespace pkpy{
|
|||||||
PyObject::~PyObject() {
|
PyObject::~PyObject() {
|
||||||
if(_attr == nullptr) return;
|
if(_attr == nullptr) return;
|
||||||
_attr->~NameDict();
|
_attr->~NameDict();
|
||||||
pool64.dealloc(_attr);
|
pool64_dealloc(_attr);
|
||||||
}
|
}
|
||||||
} // namespace pkpy
|
} // namespace pkpy
|
@ -75,12 +75,12 @@ int utf8len(unsigned char c, bool suppress){
|
|||||||
if(size <= 16){
|
if(size <= 16){
|
||||||
this->data = _inlined;
|
this->data = _inlined;
|
||||||
}else{
|
}else{
|
||||||
this->data = (char*)pool64.alloc(size);
|
this->data = (char*)pool64_alloc(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Str& Str::operator=(const Str& other){
|
Str& Str::operator=(const Str& other){
|
||||||
if(!is_inlined()) pool64.dealloc(data);
|
if(!is_inlined()) pool64_dealloc(data);
|
||||||
size = other.size;
|
size = other.size;
|
||||||
is_ascii = other.is_ascii;
|
is_ascii = other.is_ascii;
|
||||||
_cached_c_str = nullptr;
|
_cached_c_str = nullptr;
|
||||||
@ -150,7 +150,7 @@ int utf8len(unsigned char c, bool suppress){
|
|||||||
}
|
}
|
||||||
|
|
||||||
Str::~Str(){
|
Str::~Str(){
|
||||||
if(!is_inlined()) pool64.dealloc(data);
|
if(!is_inlined()) pool64_dealloc(data);
|
||||||
if(_cached_c_str != nullptr) free((void*)_cached_c_str);
|
if(_cached_c_str != nullptr) free((void*)_cached_c_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ Tuple::Tuple(int n){
|
|||||||
if(n <= 3){
|
if(n <= 3){
|
||||||
this->_args = _inlined;
|
this->_args = _inlined;
|
||||||
}else{
|
}else{
|
||||||
this->_args = (PyObject**)pool64.alloc(n * sizeof(void*));
|
this->_args = (PyObject**)pool64_alloc(n * sizeof(void*));
|
||||||
}
|
}
|
||||||
this->_size = n;
|
this->_size = n;
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ Tuple::Tuple(std::initializer_list<PyObject*> list): Tuple(list.size()){
|
|||||||
for(PyObject* obj: list) _args[i++] = obj;
|
for(PyObject* obj: list) _args[i++] = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple::~Tuple(){ if(!is_inlined()) pool64.dealloc(_args); }
|
Tuple::~Tuple(){ if(!is_inlined()) pool64_dealloc(_args); }
|
||||||
|
|
||||||
List ArgsView::to_list() const{
|
List ArgsView::to_list() const{
|
||||||
List ret(size());
|
List ret(size());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user