blueloveTH 2 лет назад
Родитель
Сommit
6a27bc8bda
5 измененных файлов с 240 добавлено и 7 удалено
  1. 1 0
      .gitignore
  2. 1 1
      src/ceval.h
  3. 3 2
      src/common.h
  4. 12 4
      src/gc.h
  5. 223 0
      src/memory.h

+ 1 - 0
.gitignore

@@ -23,3 +23,4 @@ plugins/godot/godot-cpp/
 src/_generated.h
 src/_generated.h
 profile.sh
 profile.sh
 test
 test
+tmp.rar

+ 1 - 1
src/ceval.h

@@ -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();

+ 3 - 2
src/common.h

@@ -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_NO_AUTO_GC			1
+#define DEBUG_FULL_EXCEPTION		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__)

+ 12 - 4
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 - 0
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