From 99fbb7c7367c06e948cd7d50a23032487a3f992d Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 12 Oct 2023 21:30:41 +0800 Subject: [PATCH] refactor `NameDict` --- include/pocketpy/codeobject.h | 4 +- include/pocketpy/namedict.h | 207 +++++++++++++++++++++------------- include/pocketpy/obj.h | 21 ++-- src/ceval.cpp | 12 +- src/obj.cpp | 2 +- src/pocketpy.cpp | 2 +- 6 files changed, 149 insertions(+), 99 deletions(-) diff --git a/include/pocketpy/codeobject.h b/include/pocketpy/codeobject.h index 3dca8979..a128ef10 100644 --- a/include/pocketpy/codeobject.h +++ b/include/pocketpy/codeobject.h @@ -194,7 +194,7 @@ struct Py_ final: PyObject { Function _value; template Py_(Type type, Args&&... args): PyObject(type), _value(std::forward(args)...) { - enable_instance_dict(); + _enable_instance_dict(); } void _obj_gc_mark() override { _value.decl->_gc_mark(); @@ -212,7 +212,7 @@ struct Py_ final: PyObject { NativeFunc _value; template Py_(Type type, Args&&... args): PyObject(type), _value(std::forward(args)...) { - enable_instance_dict(); + _enable_instance_dict(); } void _obj_gc_mark() override { if(_value.decl != nullptr){ diff --git a/include/pocketpy/namedict.h b/include/pocketpy/namedict.h index 6a75bacc..b7ff23df 100644 --- a/include/pocketpy/namedict.h +++ b/include/pocketpy/namedict.h @@ -20,61 +20,59 @@ struct SmallNameDict{ static const int kCapacity = 12; - int _size; - std::pair _items[kCapacity]; + bool _is_small; + uint16_t _size; + K _keys[kCapacity]; + V _values[kCapacity]; - SmallNameDict(): _size(0) {} - - void set(K key, V val){ - for(int i=0; i(); } + V* try_get_2(K key) { + for(int i=0; i void apply(Func func) const { for(int i=0; i& keys); template -struct NameDictImpl { +struct LargeNameDict { using Item = std::pair; - static constexpr uint16_t __Capacity = 8; - // ensure the initial capacity is ok for memory pool + static constexpr uint16_t __Capacity = 32; static_assert(is_pod::value); - static_assert(sizeof(Item) * __Capacity <= 128); + bool _is_small; float _load_factor; uint16_t _capacity; uint16_t _size; @@ -144,34 +141,36 @@ while(!_items[i].first.empty()) { \ } #define NAMEDICT_ALLOC() \ - _items = (Item*)pool128_alloc(_capacity * sizeof(Item)); \ + _items = (Item*)malloc(_capacity * sizeof(Item)); \ memset(_items, 0, _capacity * sizeof(Item)); \ - NameDictImpl(float load_factor=0.67f): + LargeNameDict(float load_factor=0.67f): + _is_small(false), _load_factor(load_factor), _capacity(__Capacity), _size(0), _hash_seed(kHashSeeds[0]), _mask(__Capacity-1) { NAMEDICT_ALLOC() } - NameDictImpl(const NameDictImpl& other) { - memcpy(this, &other, sizeof(NameDictImpl)); + LargeNameDict(const LargeNameDict& other) { + memcpy(this, &other, sizeof(LargeNameDict)); NAMEDICT_ALLOC() for(int i=0; i<_capacity; i++) _items[i] = other._items[i]; } - NameDictImpl& operator=(const NameDictImpl& other) { - pool128_dealloc(_items); - memcpy(this, &other, sizeof(NameDictImpl)); + LargeNameDict& operator=(const LargeNameDict& other) { + free(_items); + memcpy(this, &other, sizeof(LargeNameDict)); NAMEDICT_ALLOC() for(int i=0; i<_capacity; i++) _items[i] = other._items[i]; return *this; } - ~NameDictImpl(){ pool128_dealloc(_items); } + ~LargeNameDict(){ free(_items); } - NameDictImpl(NameDictImpl&&) = delete; - NameDictImpl& operator=(NameDictImpl&&) = delete; + LargeNameDict(LargeNameDict&&) = delete; + LargeNameDict& operator=(LargeNameDict&&) = delete; uint16_t size() const { return _size; } + uint16_t capacity() const { return _capacity; } T operator[](StrName key) const { bool ok; uint16_t i; @@ -209,7 +208,7 @@ while(!_items[i].first.empty()) { \ if(ok) FATAL_ERROR(); _items[j] = old_items[i]; } - pool128_dealloc(old_items); + free(old_items); } void _try_perfect_rehash(){ @@ -220,11 +219,7 @@ while(!_items[i].first.empty()) { \ T try_get(StrName key) const{ bool ok; uint16_t i; HASH_PROBE_0(key, ok, i); - if(!ok){ - if constexpr(std::is_pointer_v) return nullptr; - else if constexpr(std::is_same_v) return -1; - else return Discarded(); - } + if(!ok) return default_invalid_value(); return _items[i].second; } @@ -235,43 +230,20 @@ while(!_items[i].first.empty()) { \ return &_items[i].second; } - bool try_set(StrName key, T val){ - bool ok; uint16_t i; - HASH_PROBE_1(key, ok, i); - if(!ok) return false; - _items[i].second = val; - return true; - } - bool contains(StrName key) const { bool ok; uint16_t i; HASH_PROBE_0(key, ok, i); return ok; } - void update(const NameDictImpl& other){ - for(uint16_t i=0; i items() const { - std::vector v; - for(uint16_t i=0; i<_capacity; i++){ - if(_items[i].first.empty()) continue; - v.push_back(_items[i]); - } - return v; + return true; } template @@ -303,8 +275,89 @@ while(!_items[i].first.empty()) { \ #undef _hash }; +template +struct NameDictImpl{ + PK_ALWAYS_PASS_BY_POINTER(NameDictImpl) + + union{ + SmallNameDict _small; + LargeNameDict _large; + }; + + NameDictImpl(): _small() {} + NameDictImpl(float load_factor): _large(load_factor) {} + + bool is_small() const{ + const bool* p = reinterpret_cast(this); + return *p; + } + + void set(StrName key, V val){ + if(is_small()){ + bool ok = _small.try_set(key, val); + if(!ok){ + SmallNameDict copied(_small); + // move to large name dict + new (&_large) LargeNameDict(); + copied.apply([&](StrName key, V val){ + _large.set(key, val); + }); + _large.set(key, val); + } + }else{ + _large.set(key, val); + } + } + + uint16_t size() const{ return is_small() ?_small.size() : _large.size(); } + uint16_t capacity() const{ return is_small() ?_small.capacity() : _large.capacity(); } + V operator[](StrName key) const { return is_small() ?_small[key] : _large[key]; } + V try_get(StrName key) const { return is_small() ?_small.try_get(key) : _large.try_get(key); } + V* try_get_2(StrName key) { return is_small() ?_small.try_get_2(key) : _large.try_get_2(key); } + bool contains(StrName key) const { return is_small() ?_small.contains(key) : _large.contains(key); } + bool del(StrName key){ return is_small() ?_small.del(key) : _large.del(key); } + + void clear(){ + if(is_small()) _small.clear(); + else _large.clear(); + } + + template + void apply(Func func) const { + if(is_small()) _small.apply(func); + else _large.apply(func); + } + + void _try_perfect_rehash(){ + if(is_small()) return; + _large._try_perfect_rehash(); + } + + std::vector keys() const{ + std::vector v; + apply([&](StrName key, V val){ + v.push_back(key); + }); + return v; + } + + std::vector> items() const{ + std::vector> v; + apply([&](StrName key, V val){ + v.push_back({key, val}); + }); + return v; + } + + ~NameDictImpl(){ + if(!is_small()) _large.~LargeNameDict(); + } +}; + using NameDict = NameDictImpl; using NameDict_ = std::shared_ptr; using NameDictInt = NameDictImpl; +static_assert(sizeof(NameDict) <= 128); + } // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/obj.h b/include/pocketpy/obj.h index 265908a0..df995fa5 100644 --- a/include/pocketpy/obj.h +++ b/include/pocketpy/obj.h @@ -151,8 +151,12 @@ struct PyObject{ virtual ~PyObject(); - void enable_instance_dict(float lf=kInstAttrLoadFactor) { - _attr = new(pool64_alloc()) NameDict(lf); + void _enable_instance_dict() { + _attr = new(pool128_alloc()) NameDict(); + } + + void _enable_instance_dict(float lf){ + _attr = new(pool128_alloc()) NameDict(lf); } }; @@ -230,10 +234,9 @@ struct MappingProxy{ inline void gc_mark_namedict(NameDict& t){ if(t.size() == 0) return; - for(uint16_t i=0; i final: PyObject { template<> struct Py_ final: PyObject { Py_(Type type): PyObject(type) { - enable_instance_dict(); + _enable_instance_dict(); } void _obj_gc_mark() override {} void* _value_ptr() override { return nullptr; } @@ -408,7 +411,7 @@ template<> struct Py_ final: PyObject { Type _value; Py_(Type type, Type val): PyObject(type), _value(val) { - enable_instance_dict(kTypeAttrLoadFactor); + _enable_instance_dict(kTypeAttrLoadFactor); } void _obj_gc_mark() override {} void* _value_ptr() override { return &_value; } @@ -417,7 +420,7 @@ struct Py_ final: PyObject { template<> struct Py_ final: PyObject { Py_(Type type): PyObject(type) { - enable_instance_dict(kTypeAttrLoadFactor); + _enable_instance_dict(kTypeAttrLoadFactor); } void _obj_gc_mark() override {} void* _value_ptr() override { return nullptr; } diff --git a/src/ceval.cpp b/src/ceval.cpp index c3605590..05d86e98 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -210,24 +210,18 @@ __NEXT_STEP:; if(slot == nullptr) vm->UnboundLocalError(_name); *slot = PY_NULL; }else{ - if(!frame->f_globals().contains(_name)) vm->NameError(_name); - frame->f_globals().erase(_name); + if(!frame->f_globals().del(_name)) vm->NameError(_name); } DISPATCH(); TARGET(DELETE_GLOBAL) _name = StrName(byte.arg); - if(frame->f_globals().contains(_name)){ - frame->f_globals().erase(_name); - }else{ - NameError(_name); - } + if(!frame->f_globals().del(_name)) vm->NameError(_name); DISPATCH(); TARGET(DELETE_ATTR) _0 = POPX(); _name = StrName(byte.arg); if(is_tagged(_0) || !_0->is_attr_valid()) TypeError("cannot delete attribute"); - if(!_0->attr().contains(_name)) AttributeError(_0, _name); - _0->attr().erase(_name); + if(!_0->attr().del(_name)) AttributeError(_0, _name); DISPATCH(); TARGET(DELETE_SUBSCR) _1 = POPX(); diff --git a/src/obj.cpp b/src/obj.cpp index c67d9d29..6f048d2b 100644 --- a/src/obj.cpp +++ b/src/obj.cpp @@ -4,6 +4,6 @@ namespace pkpy{ PyObject::~PyObject() { if(_attr == nullptr) return; _attr->~NameDict(); - pool64_dealloc(_attr); + pool128_dealloc(_attr); } } // namespace pkpy \ No newline at end of file diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index f6832104..76e4b5c4 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -377,7 +377,7 @@ void init_builtins(VM* _vm) { if(self->is_attr_valid()){ vm->TypeError("object: instance dict is already enabled"); } - self->enable_instance_dict(); + self->_enable_instance_dict(); return vm->None; });