object pool impl

This commit is contained in:
blueloveTH 2023-02-10 01:39:29 +08:00
parent 01be71f0ef
commit e816ceee6c
9 changed files with 191 additions and 108 deletions

View File

@ -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
#define DUMMY_VAL (char)1
#define DUMMY_VAL_TP char
template<typename T>
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<typename T>
void* obj_tid(void* alt){
if constexpr(std::is_same_v<T, DUMMY_VAL_TP>) return alt;
return tid<T>();
}

View File

@ -22,16 +22,14 @@ public:
}
};
class VectorIter : public BaseIter {
template <typename T>
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 {

View File

@ -3,17 +3,25 @@
#include "common.h"
namespace pkpy{
template<typename T>
struct sp_deleter {
inline static void call(int* counter){
((T*)(counter + 1))->~T();
free(counter);
}
};
template <typename T>
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<T>::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();
}

View File

@ -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<T>((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; }
#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<void*, std::vector<int*>> _obj_pool;
namespace pkpy {
template<>
struct sp_deleter<PyObject> {
inline static void call(int* counter) {
PyObject* obj = (PyObject*)(counter + 1);
std::vector<int*>& pool = _obj_pool[obj->type_id()];
if(pool.size() > 100){
obj->~PyObject();
free(counter);
}else{
pool.push_back(counter);
}
}
};
}

View File

@ -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<BaseIter, VectorIter>(vm, args[0]));
_vm->bind_method<0>("list", "__iter__", [](VM* vm, const pkpy::Args& args) {
return vm->PyIter(pkpy::make_shared<BaseIter, ArrayIter<PyVarList>>(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<BaseIter, ArrayIter<pkpy::Args>>(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 ************/

View File

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

View File

@ -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<PyVar> 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<T2>(b);
return ret;
}
}
}
typedef pkpy::Args _Tuple;

View File

@ -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<uint16_t>(*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;

118
src/vm.h
View File

@ -20,14 +20,14 @@
class VM {
std::vector<PyVar> _small_integers; // [-5, 256]
// std::vector<PyVar> _small_integers; // [-5, 256]
std::stack< std::unique_ptr<Frame> > 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; i<items.size(); i++){
if(!items[i]->is_type(_tp_ref)) {
done = true;
PyVarList values = items.to_list();
for(int j=i; j<values.size(); j++) frame->try_deref(this, values[j]);
frame->push(PyTuple(values));
for(int j=i; j<items.size(); j++) frame->try_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<typename T>
inline PyVar new_object(PyVar type, T _value) {
if(!type->is_type(_tp_type)) UNREACHABLE();
return pkpy::make_shared<PyObject, Py_<T>>(type, _value);
std::vector<int*>& pool = _obj_pool[obj_tid<T>((void*)type.get())];
if(pool.empty()) return pkpy::make_shared<PyObject, Py_<T>>(type, _value);
int* counter = pool.back(); pool.pop_back();
*counter = 1;
Py_<T>* obj = (Py_<T>*)(counter + 1);
obj->_value = std::move(_value);
obj->attribs.clear();
return PyVar(counter);
}
template<typename T, typename... Args>
@ -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<BaseIter>, _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<PyObject, Py_<i64>>(nullptr, DUMMY_VAL);
_tp_type = pkpy::make_shared<PyObject, Py_<i64>>(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<items.size(); i++) {
i64 y = hash(items[i]);
x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 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; i<objs.size(); i++) vm->PyRef_AS_C(objs[i])->del(vm, frame);
}
/***** Frame's Impl *****/