From e816ceee6ce3ccb4e560772ccdc667c3265690b3 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 10 Feb 2023 01:39:29 +0800 Subject: [PATCH] object pool impl --- src/common.h | 19 +++++++- src/iter.h | 14 +++--- src/memory.h | 12 ++++- src/obj.h | 28 ++++++++++-- src/pocketpy.h | 70 +++++++++++++++++++---------- src/ref.h | 15 ++++--- src/safestl.h | 16 +++++-- src/str.h | 7 ++- src/vm.h | 118 ++++++++++++++++++++++++++----------------------- 9 files changed, 191 insertions(+), 108 deletions(-) diff --git a/src/common.h b/src/common.h index 409db01a..acf66c4d 100644 --- a/src/common.h +++ b/src/common.h @@ -31,8 +31,23 @@ #define UNREACHABLE() throw std::runtime_error( __FILE__ + std::string(":") + std::to_string(__LINE__) + " UNREACHABLE()!"); #endif -#define PK_VERSION "0.8.3" +#define PK_VERSION "0.8.4" typedef int64_t i64; typedef double f64; -#define DUMMY_VAL (i64)0 \ No newline at end of file +#define DUMMY_VAL (char)1 +#define DUMMY_VAL_TP char + +template +void* tid() { + static volatile int8_t _x; + return (void*)(&_x); +} + +// This does not ensure to be unique when the pointer of obj->type is deleted & reused. +// But it is good enough for now. +template +void* obj_tid(void* alt){ + if constexpr(std::is_same_v) return alt; + return tid(); +} \ No newline at end of file diff --git a/src/iter.h b/src/iter.h index 245cadd1..6ea54239 100644 --- a/src/iter.h +++ b/src/iter.h @@ -22,16 +22,14 @@ public: } }; -class VectorIter : public BaseIter { +template +class ArrayIter : public BaseIter { size_t index = 0; - const PyVarList* vec; + const T* p; public: - VectorIter(VM* vm, PyVar _ref) : BaseIter(vm, _ref) { - vec = &OBJ_GET(PyVarList, _ref); - } - - bool hasNext(){ return index < vec->size(); } - PyVar next(){ return vec->operator[](index++); } + ArrayIter(VM* vm, PyVar _ref) : BaseIter(vm, _ref) { p = &OBJ_GET(T, _ref);} + bool hasNext(){ return index < p->size(); } + PyVar next(){ return p->operator[](index++); } }; class StringIter : public BaseIter { diff --git a/src/memory.h b/src/memory.h index 8178770f..4bfb37c3 100644 --- a/src/memory.h +++ b/src/memory.h @@ -3,17 +3,25 @@ #include "common.h" namespace pkpy{ + template + struct sp_deleter { + inline static void call(int* counter){ + ((T*)(counter + 1))->~T(); + free(counter); + } + }; + template class shared_ptr { int* counter = nullptr; #define _t() ((T*)(counter + 1)) #define _inc_counter() if(counter) ++(*counter) -#define _dec_counter() if(counter && --(*counter) == 0){ _t()->~T(); free(counter); } +#define _dec_counter() if(counter && --(*counter) == 0){ pkpy::sp_deleter::call(counter); } public: shared_ptr() {} - shared_ptr(int* block) : counter(block) {} + shared_ptr(int* counter) : counter(counter) {} shared_ptr(const shared_ptr& other) : counter(other.counter) { _inc_counter(); } diff --git a/src/obj.h b/src/obj.h index 9fef7584..aba7e8b1 100644 --- a/src/obj.h +++ b/src/obj.h @@ -78,7 +78,8 @@ struct PyObject { PyVarDict attribs; inline bool is_type(const PyVar& type) const noexcept{ return this->type == type; } - inline virtual void* value() = 0; + virtual void* value() = 0; + virtual void* type_id() = 0; PyObject(const PyVar& type) : type(type) {} virtual ~PyObject() = default; @@ -89,7 +90,8 @@ struct Py_ : PyObject { T _value; Py_(const PyVar& type, T val) : PyObject(type), _value(val) {} - virtual void* value() override { return &_value; } + void* value() override { return &_value; } + void* type_id() override { return obj_tid((void*)type.get()); } }; // Unsafe cast from PyObject to C++ type @@ -102,4 +104,24 @@ struct Py_ : PyObject { inline static const char* _mod() { return #mod; } \ inline static const char* _name() { return #name; } -#define PY_BUILTIN_CLASS(name) inline static PyVar _type(VM* vm) { return vm->_tp_##name; } \ No newline at end of file +#define PY_BUILTIN_CLASS(name) inline static PyVar _type(VM* vm) { return vm->_tp_##name; } + + +// memory pool _tp -> [obj1, obj2, ...] +static thread_local emhash8::HashMap> _obj_pool; + +namespace pkpy { + template<> + struct sp_deleter { + inline static void call(int* counter) { + PyObject* obj = (PyObject*)(counter + 1); + std::vector& pool = _obj_pool[obj->type_id()]; + if(pool.size() > 100){ + obj->~PyObject(); + free(counter); + }else{ + pool.push_back(counter); + } + } + }; +} \ No newline at end of file diff --git a/src/pocketpy.h b/src/pocketpy.h index c6c734cf..ea4928d9 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -370,20 +370,23 @@ void init_builtins(VM* _vm) { }); _vm->bind_method<1>("str", "join", [](VM* vm, const pkpy::Args& args) { - const _Str& _self = vm->PyStr_AS_C(args[0]); - PyVarList* _list = nullptr; + const _Str& self = vm->PyStr_AS_C(args[0]); + _StrStream ss; if(args[1]->is_type(vm->_tp_list)){ - _list = &vm->PyList_AS_C(args[1]); + const PyVarList& a = vm->PyList_AS_C(args[1]); + for(int i = 0; i < a.size(); i++){ + if(i > 0) ss << self; + ss << vm->PyStr_AS_C(vm->asStr(a[i])); + } }else if(args[1]->is_type(vm->_tp_tuple)){ - _list = &vm->PyTuple_AS_C(args[1]); + const _Tuple& a = vm->PyTuple_AS_C(args[1]); + for(int i = 0; i < a.size(); i++){ + if(i > 0) ss << self; + ss << vm->PyStr_AS_C(vm->asStr(a[i])); + } }else{ vm->TypeError("can only join a list or tuple"); } - _StrStream ss; - for(int i = 0; i < _list->size(); i++){ - if(i > 0) ss << _self; - ss << vm->PyStr_AS_C(vm->asStr(_list->operator[](i))); - } return vm->PyStr(ss.str()); }); @@ -426,25 +429,24 @@ void init_builtins(VM* _vm) { return vm->PyInt(_self.size()); }); - _vm->_bind_methods<0>({"list", "tuple"}, "__iter__", [](VM* vm, const pkpy::Args& args) { - return vm->PyIter(pkpy::make_shared(vm, args[0])); + _vm->bind_method<0>("list", "__iter__", [](VM* vm, const pkpy::Args& args) { + return vm->PyIter(pkpy::make_shared>(vm, args[0])); }); - _vm->_bind_methods<1>({"list", "tuple"}, "__getitem__", [](VM* vm, const pkpy::Args& args) { - bool list = args[0]->is_type(vm->_tp_list); - const PyVarList& _self = list ? vm->PyList_AS_C(args[0]) : vm->PyTuple_AS_C(args[0]); + _vm->bind_method<1>("list", "__getitem__", [](VM* vm, const pkpy::Args& args) { + const PyVarList& self = vm->PyList_AS_C(args[0]); if(args[1]->is_type(vm->_tp_slice)){ _Slice s = vm->PySlice_AS_C(args[1]); - s.normalize(_self.size()); - PyVarList _new_list; - for(size_t i = s.start; i < s.stop; i++) _new_list.push_back(_self[i]); - return list ? vm->PyList(_new_list) : vm->PyTuple(_new_list); + s.normalize(self.size()); + PyVarList new_list; + for(size_t i = s.start; i < s.stop; i++) new_list.push_back(self[i]); + return vm->PyList(std::move(new_list)); } - int _index = (int)vm->PyInt_AS_C(args[1]); - _index = vm->normalized_index(_index, _self.size()); - return _self[_index]; + int index = (int)vm->PyInt_AS_C(args[1]); + index = vm->normalized_index(index, self.size()); + return self[index]; }); _vm->bind_method<2>("list", "__setitem__", [](VM* vm, const pkpy::Args& args) { @@ -466,12 +468,32 @@ void init_builtins(VM* _vm) { /************ PyTuple ************/ _vm->bind_static_method<1>("tuple", "__new__", [](VM* vm, const pkpy::Args& args) { PyVarList _list = vm->PyList_AS_C(vm->call(vm->builtins->attribs["list"], args)); - return vm->PyTuple(_list); + return vm->PyTuple(std::move(_list)); + }); + + _vm->bind_method<0>("tuple", "__iter__", [](VM* vm, const pkpy::Args& args) { + return vm->PyIter(pkpy::make_shared>(vm, args[0])); + }); + + _vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, const pkpy::Args& args) { + const _Tuple& self = vm->PyTuple_AS_C(args[0]); + + if(args[1]->is_type(vm->_tp_slice)){ + _Slice s = vm->PySlice_AS_C(args[1]); + s.normalize(self.size()); + PyVarList new_list; + for(size_t i = s.start; i < s.stop; i++) new_list.push_back(self[i]); + return vm->PyTuple(std::move(new_list)); + } + + int index = (int)vm->PyInt_AS_C(args[1]); + index = vm->normalized_index(index, self.size()); + return self[index]; }); _vm->bind_method<0>("tuple", "__len__", [](VM* vm, const pkpy::Args& args) { - const PyVarList& _self = vm->PyTuple_AS_C(args[0]); - return vm->PyInt(_self.size()); + const _Tuple& self = vm->PyTuple_AS_C(args[0]); + return vm->PyInt(self.size()); }); /************ PyBool ************/ diff --git a/src/ref.h b/src/ref.h index bb1cd6cd..5fa94baa 100644 --- a/src/ref.h +++ b/src/ref.h @@ -17,8 +17,10 @@ enum NameScope { }; struct NameRef : BaseRef { - const std::pair<_Str, NameScope>* pair; - NameRef(const std::pair<_Str, NameScope>& pair) : pair(&pair) {} + std::pair<_Str, NameScope>* _pair; + inline const _Str& name() const { return _pair->first; } + inline NameScope scope() const { return _pair->second; } + NameRef(std::pair<_Str, NameScope>& pair) : _pair(&pair) {} PyVar get(VM* vm, Frame* frame) const; void set(VM* vm, Frame* frame, PyVar val) const; @@ -27,8 +29,8 @@ struct NameRef : BaseRef { struct AttrRef : BaseRef { mutable PyVar obj; - const NameRef attr; - AttrRef(PyVar obj, const NameRef attr) : obj(obj), attr(attr) {} + NameRef attr; + AttrRef(PyVar obj, NameRef attr) : obj(obj), attr(attr) {} PyVar get(VM* vm, Frame* frame) const; void set(VM* vm, Frame* frame, PyVar val) const; @@ -46,9 +48,8 @@ struct IndexRef : BaseRef { }; struct TupleRef : BaseRef { - PyVarList varRefs; - TupleRef(const PyVarList& varRefs) : varRefs(varRefs) {} - TupleRef(PyVarList&& varRefs) : varRefs(std::move(varRefs)) {} + _Tuple objs; + TupleRef(_Tuple&& objs) : objs(std::move(objs)) {} PyVar get(VM* vm, Frame* frame) const; void set(VM* vm, Frame* frame, PyVar val) const; diff --git a/src/safestl.h b/src/safestl.h index f99e31c6..c8667959 100644 --- a/src/safestl.h +++ b/src/safestl.h @@ -58,7 +58,7 @@ namespace pkpy { } } - void _release(){ + void _free(){ if(_size == 0 || _args == nullptr) return; if(_size >= kMaxPoolSize || _args_pool[_size].size() > 32){ delete[] _args; @@ -76,6 +76,12 @@ namespace pkpy { for(int i=0; i<_size; i++) _args[i] = other._args[i]; } + Args(std::initializer_list a){ + _alloc(a.size()); + int i = 0; + for(auto& v: a) _args[i++] = v; + } + Args(Args&& other) noexcept { this->_args = other._args; this->_size = other._size; @@ -93,7 +99,7 @@ namespace pkpy { const PyVar& operator[](int i) const { return _args[i]; } Args& operator=(Args&& other) noexcept { - _release(); + _free(); this->_args = other._args; this->_size = other._size; other._args = nullptr; @@ -126,7 +132,7 @@ namespace pkpy { } } - ~Args(){ _release(); } + ~Args(){ _free(); } }; const Args& no_arg(){ @@ -148,4 +154,6 @@ namespace pkpy { ret[1] = std::forward(b); return ret; } -} \ No newline at end of file +} + +typedef pkpy::Args _Tuple; \ No newline at end of file diff --git a/src/str.h b/src/str.h index 0efdd119..094978c9 100644 --- a/src/str.h +++ b/src/str.h @@ -117,8 +117,10 @@ public: _Str& operator=(const _Str& s){ this->std::string::operator=(s); - if(_u8_index != nullptr) delete _u8_index; - this->_u8_index = s._u8_index; + if(_u8_index != nullptr){ + delete _u8_index; + _u8_index = new std::vector(*s._u8_index); + } this->hash_initialized = s.hash_initialized; this->_hash = s._hash; return *this; @@ -128,6 +130,7 @@ public: this->std::string::operator=(std::move(s)); if(_u8_index != nullptr) delete _u8_index; this->_u8_index = s._u8_index; + s._u8_index = nullptr; this->hash_initialized = s.hash_initialized; this->_hash = s._hash; return *this; diff --git a/src/vm.h b/src/vm.h index 0784bb55..82bda6d5 100644 --- a/src/vm.h +++ b/src/vm.h @@ -20,14 +20,14 @@ class VM { - std::vector _small_integers; // [-5, 256] + // std::vector _small_integers; // [-5, 256] std::stack< std::unique_ptr > callstack; PyVar _py_op_call; - + PyVar run_frame(Frame* frame){ while(frame->has_next_bytecode()){ const Bytecode& byte = frame->next_bytecode(); - // if(frame->_module != builtins){ + // if(true || frame->_module != builtins){ // printf("%d: %s (%d) %s\n", frame->_ip, OP_NAMES[byte.op], byte.arg, frame->stack_info().c_str()); // } switch (byte.op) @@ -46,11 +46,11 @@ class VM { frame->push(NameRef(frame->co->names[byte.arg]).get(this, frame)); } break; case OP_STORE_NAME: { - const auto& p = frame->co->names[byte.arg]; + auto& p = frame->co->names[byte.arg]; NameRef(p).set(this, frame, frame->pop_value(this)); } break; case OP_BUILD_ATTR_REF: { - const auto& attr = frame->co->names[byte.arg]; + auto& attr = frame->co->names[byte.arg]; PyVar obj = frame->pop_value(this); frame->push(PyRef(AttrRef(obj, NameRef(attr)))); } break; @@ -75,14 +75,13 @@ class VM { for(int i=0; iis_type(_tp_ref)) { done = true; - PyVarList values = items.to_list(); - for(int j=i; jtry_deref(this, values[j]); - frame->push(PyTuple(values)); + for(int j=i; jtry_deref(this, items[j]); + frame->push(PyTuple(std::move(items))); break; } } if(done) break; - frame->push(PyRef(TupleRef(items.to_list()))); + frame->push(PyRef(TupleRef(std::move(items)))); } break; case OP_BUILD_STRING: { @@ -364,10 +363,9 @@ public: this->_stdout = new _StrStream(); this->_stderr = new _StrStream(); } - initializeBuiltinClasses(); - _small_integers.reserve(270); - for(i64 i=-5; i<=256; i++) _small_integers.push_back(new_object(_tp_int, i)); + init_builtin_types(); + // for(i64 i=-5; i<=256; i++) _small_integers.push_back(new_object(_tp_int, i)); } PyVar asStr(const PyVar& obj){ @@ -473,8 +471,7 @@ public: std::vector<_Str> positional_overrided_keys; if(!fn->starredArg.empty()){ - // handle *args - PyVarList vargs; + PyVarList vargs; // handle *args while(i < args.size()) vargs.push_back(args[i++]); locals.emplace(fn->starredArg, PyTuple(std::move(vargs))); }else{ @@ -608,7 +605,14 @@ public: template inline PyVar new_object(PyVar type, T _value) { if(!type->is_type(_tp_type)) UNREACHABLE(); - return pkpy::make_shared>(type, _value); + std::vector& pool = _obj_pool[obj_tid((void*)type.get())]; + if(pool.empty()) return pkpy::make_shared>(type, _value); + int* counter = pool.back(); pool.pop_back(); + *counter = 1; + Py_* obj = (Py_*)(counter + 1); + obj->_value = std::move(_value); + obj->attribs.clear(); + return PyVar(counter); } template @@ -810,16 +814,11 @@ public: return (const BaseRef*)(obj->value()); } - __DEF_PY_AS_C(Int, i64, _tp_int) - inline PyVar PyInt(i64 value) { - if(value >= -5 && value <= 256) return _small_integers[value + 5]; - return new_object(_tp_int, value); - } - + DEF_NATIVE(Int, i64, _tp_int) DEF_NATIVE(Float, f64, _tp_float) DEF_NATIVE(Str, _Str, _tp_str) DEF_NATIVE(List, PyVarList, _tp_list) - DEF_NATIVE(Tuple, PyVarList, _tp_tuple) + DEF_NATIVE(Tuple, _Tuple, _tp_tuple) DEF_NATIVE(Function, _Func, _tp_function) DEF_NATIVE(NativeFunction, _CppFunc, _tp_native_function) DEF_NATIVE(Iter, pkpy::shared_ptr, _tp_native_iterator) @@ -832,7 +831,7 @@ public: inline bool PyBool_AS_C(const PyVar& obj){return obj == True;} inline const PyVar& PyBool(bool value){return value ? True : False;} - void initializeBuiltinClasses(){ + void init_builtin_types(){ _tp_object = pkpy::make_shared>(nullptr, DUMMY_VAL); _tp_type = pkpy::make_shared>(nullptr, DUMMY_VAL); _types["object"] = _tp_object; @@ -890,8 +889,9 @@ public: if (obj->is_type(_tp_type)) return (i64)obj.get(); if (obj->is_type(_tp_tuple)) { i64 x = 1000003; - for (const auto& item : PyTuple_AS_C(obj)) { - i64 y = hash(item); + const _Tuple& items = PyTuple_AS_C(obj); + for (int i=0; i> 2)); // recommended by Github Copilot } return x; @@ -964,26 +964,26 @@ public: /***** Pointers' Impl *****/ PyVar NameRef::get(VM* vm, Frame* frame) const{ PyVar* val; - val = frame->f_locals().try_get(pair->first); + val = frame->f_locals().try_get(name()); if(val) return *val; - val = frame->f_globals().try_get(pair->first); + val = frame->f_globals().try_get(name()); if(val) return *val; - val = vm->builtins->attribs.try_get(pair->first); + val = vm->builtins->attribs.try_get(name()); if(val) return *val; - vm->NameError(pair->first); + vm->NameError(name()); return nullptr; } void NameRef::set(VM* vm, Frame* frame, PyVar val) const{ - switch(pair->second) { - case NAME_LOCAL: frame->f_locals()[pair->first] = std::move(val); break; + switch(scope()) { + case NAME_LOCAL: frame->f_locals()[name()] = std::move(val); break; case NAME_GLOBAL: { - PyVar* existing = frame->f_locals().try_get(pair->first); + PyVar* existing = frame->f_locals().try_get(name()); if(existing != nullptr){ *existing = std::move(val); }else{ - frame->f_globals()[pair->first] = std::move(val); + frame->f_globals()[name()] = std::move(val); } } break; default: UNREACHABLE(); @@ -991,23 +991,23 @@ void NameRef::set(VM* vm, Frame* frame, PyVar val) const{ } void NameRef::del(VM* vm, Frame* frame) const{ - switch(pair->second) { + switch(scope()) { case NAME_LOCAL: { - if(frame->f_locals().contains(pair->first)){ - frame->f_locals().erase(pair->first); + if(frame->f_locals().contains(name())){ + frame->f_locals().erase(name()); }else{ - vm->NameError(pair->first); + vm->NameError(name()); } } break; case NAME_GLOBAL: { - if(frame->f_locals().contains(pair->first)){ - frame->f_locals().erase(pair->first); + if(frame->f_locals().contains(name())){ + frame->f_locals().erase(name()); }else{ - if(frame->f_globals().contains(pair->first)){ - frame->f_globals().erase(pair->first); + if(frame->f_globals().contains(name())){ + frame->f_globals().erase(name()); }else{ - vm->NameError(pair->first); + vm->NameError(name()); } } } break; @@ -1016,11 +1016,11 @@ void NameRef::del(VM* vm, Frame* frame) const{ } PyVar AttrRef::get(VM* vm, Frame* frame) const{ - return vm->getattr(obj, attr.pair->first); + return vm->getattr(obj, attr.name()); } void AttrRef::set(VM* vm, Frame* frame, PyVar val) const{ - vm->setattr(obj, attr.pair->first, val); + vm->setattr(obj, attr.name(), val); } void AttrRef::del(VM* vm, Frame* frame) const{ @@ -1040,27 +1040,33 @@ void IndexRef::del(VM* vm, Frame* frame) const{ } PyVar TupleRef::get(VM* vm, Frame* frame) const{ - PyVarList args(varRefs.size()); - for (int i = 0; i < varRefs.size(); i++) { - args[i] = vm->PyRef_AS_C(varRefs[i])->get(vm, frame); + _Tuple args(objs.size()); + for (int i = 0; i < objs.size(); i++) { + args[i] = vm->PyRef_AS_C(objs[i])->get(vm, frame); } - return vm->PyTuple(args); + return vm->PyTuple(std::move(args)); } void TupleRef::set(VM* vm, Frame* frame, PyVar val) const{ - if(!val->is_type(vm->_tp_tuple) && !val->is_type(vm->_tp_list)){ +#define TUPLE_REF_SET() \ + if(args.size() > objs.size()) vm->ValueError("too many values to unpack"); \ + if(args.size() < objs.size()) vm->ValueError("not enough values to unpack"); \ + for (int i = 0; i < objs.size(); i++) vm->PyRef_AS_C(objs[i])->set(vm, frame, args[i]); + + if(val->is_type(vm->_tp_tuple)){ + const _Tuple& args = OBJ_GET(_Tuple, val); + TUPLE_REF_SET() + }else if(val->is_type(vm->_tp_list)){ + const PyVarList& args = OBJ_GET(PyVarList, val); + TUPLE_REF_SET() + }else{ vm->TypeError("only tuple or list can be unpacked"); } - const PyVarList& args = OBJ_GET(PyVarList, val); - if(args.size() > varRefs.size()) vm->ValueError("too many values to unpack"); - if(args.size() < varRefs.size()) vm->ValueError("not enough values to unpack"); - for (int i = 0; i < varRefs.size(); i++) { - vm->PyRef_AS_C(varRefs[i])->set(vm, frame, args[i]); - } +#undef TUPLE_REF_SET } void TupleRef::del(VM* vm, Frame* frame) const{ - for (auto& r : varRefs) vm->PyRef_AS_C(r)->del(vm, frame); + for(int i=0; iPyRef_AS_C(objs[i])->del(vm, frame); } /***** Frame's Impl *****/