mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
up 1
up rename
This commit is contained in:
parent
fc8503cb45
commit
ee46388841
@ -102,6 +102,7 @@ PyVar VM::run_frame(Frame* frame){
|
|||||||
const pkpy::Function& f = PyFunction_AS_C(fn);
|
const pkpy::Function& f = PyFunction_AS_C(fn);
|
||||||
setattr(cls, f.name, fn);
|
setattr(cls, f.name, fn);
|
||||||
}
|
}
|
||||||
|
cls->attr()._try_perfect_rehash();
|
||||||
} continue;
|
} continue;
|
||||||
case OP_RETURN_VALUE: return frame->pop_value(this);
|
case OP_RETURN_VALUE: return frame->pop_value(this);
|
||||||
case OP_PRINT_EXPR: {
|
case OP_PRINT_EXPR: {
|
||||||
|
@ -65,7 +65,8 @@ struct CodeObject {
|
|||||||
std::vector<CodeBlock> blocks = { CodeBlock{NO_BLOCK, -1} };
|
std::vector<CodeBlock> blocks = { CodeBlock{NO_BLOCK, -1} };
|
||||||
std::map<StrName, int> labels;
|
std::map<StrName, int> labels;
|
||||||
|
|
||||||
int ideal_locals_capacity = 4;
|
uint32_t perfect_locals_capacity = 2;
|
||||||
|
uint32_t perfect_hash_seed = 0xffffffff;
|
||||||
|
|
||||||
void optimize(VM* vm);
|
void optimize(VM* vm);
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
// #include <filesystem>
|
// #include <filesystem>
|
||||||
// namespace fs = std::filesystem;
|
// namespace fs = std::filesystem;
|
||||||
|
@ -389,7 +389,7 @@ private:
|
|||||||
_compile_f_args(func, false);
|
_compile_f_args(func, false);
|
||||||
consume(TK(":"));
|
consume(TK(":"));
|
||||||
}
|
}
|
||||||
func.code = pkpy::make_shared<CodeObject>(parser->src, func.name);
|
func.code = pkpy::make_shared<CodeObject>(parser->src, func.name.str());
|
||||||
this->codes.push(func.code);
|
this->codes.push(func.code);
|
||||||
co()->_rvalue += 1; EXPR_TUPLE(); co()->_rvalue -= 1;
|
co()->_rvalue += 1; EXPR_TUPLE(); co()->_rvalue -= 1;
|
||||||
emit(OP_RETURN_VALUE);
|
emit(OP_RETURN_VALUE);
|
||||||
@ -1024,7 +1024,7 @@ __LISTCOMP:
|
|||||||
consume(TK(")"));
|
consume(TK(")"));
|
||||||
}
|
}
|
||||||
if(match(TK("->"))) consume(TK("@id")); // eat type hints
|
if(match(TK("->"))) consume(TK("@id")); // eat type hints
|
||||||
func.code = pkpy::make_shared<CodeObject>(parser->src, func.name);
|
func.code = pkpy::make_shared<CodeObject>(parser->src, func.name.str());
|
||||||
this->codes.push(func.code);
|
this->codes.push(func.code);
|
||||||
compile_block_body();
|
compile_block_body();
|
||||||
func.code->optimize(vm);
|
func.code->optimize(vm);
|
||||||
|
@ -5,6 +5,36 @@
|
|||||||
#include "str.h"
|
#include "str.h"
|
||||||
|
|
||||||
namespace pkpy{
|
namespace pkpy{
|
||||||
|
const std::vector<uint32_t> kHashSeeds = {3452556591, 3259656564, 3106121857, 2774518055, 4085946151, 4274771677, 4047908201, 2149081045, 4160441109, 4127125901, 3109730425, 2794687362, 2806137727, 2642447290, 4070996945, 3580743775, 3719956858, 2960278187, 3568486238, 3125361093, 2232173865, 4043238260, 3265527710, 2206062780, 3968387223, 3144295694, 3293736932, 3196583945, 3832534010, 3311528523, 4258510773, 4049882022, 3058077580, 2446794117, 2330081744, 2563269634, 3848248775, 2197398712, 2874906918, 3012473024, 3477039876, 2710692860, 2806508231, 3893239503, 3929140074, 3145323261, 3593960112, 2451662716, 2545939029, 2475647797, 2790321726, 4166873680, 3504262692, 3140715282, 3078827310, 3177714229, 3006241931, 3777800785, 3621627818, 3163832382, 2166076714, 3622591406, 3299007679, 2915427082, 3939911590, 4145015468, 2791077264, 3916399405, 3330576709, 2466029172, 3534773842, 2690327419, 2487859383, 3687001303, 2615131117, 3057598651, 2548471802, 3145782646, 3895406770, 2150621965, 2179753887, 2159855306, 2439700132, 2397760304, 3405860607, 4268549710, 2779408554, 2485874456, 3796299954, 4179315997, 2380599704, 3210079474, 3951990603, 3342489194, 2997361581, 3576131817, 3163713423, 2467495451, 4190562029, 2588496185};
|
||||||
|
const std::vector<uint32_t> kPrimes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599};
|
||||||
|
|
||||||
|
uint32_t find_next_prime(uint32_t n){
|
||||||
|
auto it = std::lower_bound(kPrimes.begin(), kPrimes.end(), n);
|
||||||
|
if(it == kPrimes.end()) return n;
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t _hash(StrName key, uint32_t capacity, uint32_t hash_seed){
|
||||||
|
uint32_t i = key.index * (uint32_t)2654435761;
|
||||||
|
return (i ^ hash_seed) % capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t find_perfect_hash_seed(uint32_t capacity, const std::vector<StrName>& keys){
|
||||||
|
if(keys.empty()) return 0xffffffff;
|
||||||
|
std::set<uint32_t> indices;
|
||||||
|
std::vector<std::pair<uint32_t, float>> scores;
|
||||||
|
for(int i=0; i<kHashSeeds.size(); i++){
|
||||||
|
indices.clear();
|
||||||
|
for(auto key: keys){
|
||||||
|
uint32_t index = _hash(key, capacity, kHashSeeds[i]);
|
||||||
|
indices.insert(index);
|
||||||
|
}
|
||||||
|
float score = indices.size() / (float)keys.size();
|
||||||
|
scores.push_back({kHashSeeds[i], score});
|
||||||
|
}
|
||||||
|
std::sort(scores.begin(), scores.end(), [](auto a, auto b){ return a.second > b.second; });
|
||||||
|
return scores[0].first;
|
||||||
|
}
|
||||||
|
|
||||||
struct NameDictNode{
|
struct NameDictNode{
|
||||||
StrName first;
|
StrName first;
|
||||||
@ -13,45 +43,45 @@ namespace pkpy{
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct NameDict {
|
struct NameDict {
|
||||||
int _capacity;
|
uint32_t _capacity;
|
||||||
int _size;
|
uint32_t _size;
|
||||||
float _load_factor;
|
float _load_factor;
|
||||||
|
uint32_t _hash_seed;
|
||||||
NameDictNode* _a;
|
NameDictNode* _a;
|
||||||
|
|
||||||
NameDict(int capacity=4, float load_factor=0.67):
|
NameDict(uint32_t capacity=2, float load_factor=0.67, uint32_t hash_seed=0xffffffff):
|
||||||
_capacity(capacity), _size(0), _load_factor(load_factor) {
|
_capacity(capacity), _size(0), _load_factor(load_factor),
|
||||||
_a = new NameDictNode[_capacity];
|
_hash_seed(hash_seed), _a(new NameDictNode[capacity]) {}
|
||||||
}
|
|
||||||
|
|
||||||
NameDict(const NameDict& other) {
|
NameDict(const NameDict& other) {
|
||||||
this->_capacity = other._capacity;
|
this->_capacity = other._capacity;
|
||||||
this->_size = other._size;
|
this->_size = other._size;
|
||||||
this->_load_factor = other._load_factor;
|
this->_load_factor = other._load_factor;
|
||||||
|
this->_hash_seed = other._hash_seed;
|
||||||
this->_a = new NameDictNode[_capacity];
|
this->_a = new NameDictNode[_capacity];
|
||||||
for(int i=0; i<_capacity; i++) _a[i] = other._a[i];
|
for(uint32_t i=0; i<_capacity; i++) _a[i] = other._a[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
NameDict& operator=(const NameDict&) = delete;
|
NameDict& operator=(const NameDict&) = delete;
|
||||||
NameDict(NameDict&&) = delete;
|
NameDict(NameDict&&) = delete;
|
||||||
NameDict& operator=(NameDict&&) = delete;
|
NameDict& operator=(NameDict&&) = delete;
|
||||||
|
|
||||||
int size() const { return _size; }
|
uint32_t size() const { return _size; }
|
||||||
|
|
||||||
//https://github.com/python/cpython/blob/main/Objects/dictobject.c#L175
|
|
||||||
#define HASH_PROBE(key, ok, i) \
|
#define HASH_PROBE(key, ok, i) \
|
||||||
int i = (key).index & (_capacity-1); \
|
bool ok = false; uint32_t i; \
|
||||||
bool ok = false; \
|
i = _hash(key, _capacity, _hash_seed); \
|
||||||
while(!_a[i].empty()) { \
|
while(!_a[i].empty()) { \
|
||||||
if(_a[i].first == (key)) { ok = true; break; } \
|
if(_a[i].first == (key)) { ok = true; break; } \
|
||||||
i = (5*i + 1) & (_capacity-1); \
|
i = (i + 1) % _capacity; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HASH_PROBE_OVERRIDE(key, ok, i) \
|
#define HASH_PROBE_OVERRIDE(key, ok, i) \
|
||||||
i = (key).index & (_capacity-1); \
|
|
||||||
ok = false; \
|
ok = false; \
|
||||||
|
i = _hash(key, _capacity, _hash_seed); \
|
||||||
while(!_a[i].empty()) { \
|
while(!_a[i].empty()) { \
|
||||||
if(_a[i].first == (key)) { ok = true; break; } \
|
if(_a[i].first == (key)) { ok = true; break; } \
|
||||||
i = (5*i + 1) & (_capacity-1); \
|
i = (i + 1) % _capacity; \
|
||||||
}
|
}
|
||||||
|
|
||||||
const PyVar& operator[](StrName key) const {
|
const PyVar& operator[](StrName key) const {
|
||||||
@ -66,20 +96,20 @@ namespace pkpy{
|
|||||||
_a[i].first = key;
|
_a[i].first = key;
|
||||||
_size++;
|
_size++;
|
||||||
if(_size > _capacity * _load_factor){
|
if(_size > _capacity * _load_factor){
|
||||||
_rehash_2x();
|
_rehash(true);
|
||||||
HASH_PROBE_OVERRIDE(key, ok, i);
|
HASH_PROBE_OVERRIDE(key, ok, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _a[i].second;
|
return _a[i].second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _rehash_2x(){
|
void _rehash(bool resize){
|
||||||
NameDictNode* old_a = _a;
|
NameDictNode* old_a = _a;
|
||||||
int old_capacity = _capacity;
|
uint32_t old_capacity = _capacity;
|
||||||
_capacity *= 2;
|
if(resize) _capacity = find_next_prime(_capacity * 2);
|
||||||
_size = 0;
|
_size = 0;
|
||||||
_a = new NameDictNode[_capacity];
|
_a = new NameDictNode[_capacity];
|
||||||
for(int i=0; i<old_capacity; i++){
|
for(uint32_t i=0; i<old_capacity; i++){
|
||||||
if(old_a[i].empty()) continue;
|
if(old_a[i].empty()) continue;
|
||||||
HASH_PROBE(old_a[i].first, ok, j);
|
HASH_PROBE(old_a[i].first, ok, j);
|
||||||
if(ok) UNREACHABLE();
|
if(ok) UNREACHABLE();
|
||||||
@ -90,6 +120,16 @@ namespace pkpy{
|
|||||||
delete[] old_a;
|
delete[] old_a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _try_perfect_rehash(){
|
||||||
|
std::vector<StrName> keys;
|
||||||
|
for(uint32_t i=0; i<_capacity; i++){
|
||||||
|
if(_a[i].empty()) continue;
|
||||||
|
keys.push_back(_a[i].first);
|
||||||
|
}
|
||||||
|
_hash_seed = find_perfect_hash_seed(_capacity, keys);
|
||||||
|
_rehash(false); // do not resize
|
||||||
|
}
|
||||||
|
|
||||||
inline PyVar* try_get(StrName key){
|
inline PyVar* try_get(StrName key){
|
||||||
HASH_PROBE(key, ok, i);
|
HASH_PROBE(key, ok, i);
|
||||||
if(!ok) return nullptr;
|
if(!ok) return nullptr;
|
||||||
@ -112,9 +152,9 @@ namespace pkpy{
|
|||||||
|
|
||||||
struct iterator {
|
struct iterator {
|
||||||
const NameDict* _dict;
|
const NameDict* _dict;
|
||||||
int i;
|
uint32_t i;
|
||||||
iterator() = default;
|
iterator() = default;
|
||||||
iterator(const NameDict* dict, int i): _dict(dict), i(i) { _skip_empty(); }
|
iterator(const NameDict* dict, uint32_t i): _dict(dict), i(i) { _skip_empty(); }
|
||||||
inline void _skip_empty(){ while(i < _dict->_capacity && _dict->_a[i].empty()) i++;}
|
inline void _skip_empty(){ while(i < _dict->_capacity && _dict->_a[i].empty()) i++;}
|
||||||
inline iterator& operator++(){ i++; _skip_empty(); return *this;}
|
inline iterator& operator++(){ i++; _skip_empty(); return *this;}
|
||||||
|
|
||||||
@ -131,7 +171,7 @@ namespace pkpy{
|
|||||||
_a[i].first = key;
|
_a[i].first = key;
|
||||||
_size++;
|
_size++;
|
||||||
if(_size > _capacity * _load_factor){
|
if(_size > _capacity * _load_factor){
|
||||||
_rehash_2x();
|
_rehash(true);
|
||||||
HASH_PROBE_OVERRIDE(key, ok, i);
|
HASH_PROBE_OVERRIDE(key, ok, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ struct NativeFunc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Function {
|
struct Function {
|
||||||
Str name;
|
StrName name;
|
||||||
CodeObject_ code;
|
CodeObject_ code;
|
||||||
std::vector<StrName> args;
|
std::vector<StrName> args;
|
||||||
StrName starred_arg; // empty if no *arg
|
StrName starred_arg; // empty if no *arg
|
||||||
@ -99,9 +99,9 @@ struct Py_ : PyObject {
|
|||||||
|
|
||||||
inline void _init() noexcept {
|
inline void _init() noexcept {
|
||||||
if constexpr (std::is_same_v<T, Type> || std::is_same_v<T, DummyModule>) {
|
if constexpr (std::is_same_v<T, Type> || std::is_same_v<T, DummyModule>) {
|
||||||
_attr = new pkpy::NameDict(8, kTypeAttrLoadFactor);
|
_attr = new pkpy::NameDict(5, kTypeAttrLoadFactor);
|
||||||
}else if constexpr(std::is_same_v<T, DummyInstance>){
|
}else if constexpr(std::is_same_v<T, DummyInstance>){
|
||||||
_attr = new pkpy::NameDict(4, kInstAttrLoadFactor);
|
_attr = new pkpy::NameDict(5, kInstAttrLoadFactor);
|
||||||
}else{
|
}else{
|
||||||
_attr = nullptr;
|
_attr = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ bool is_unicode_Lo_char(uint32_t c) {
|
|||||||
|
|
||||||
|
|
||||||
struct StrName {
|
struct StrName {
|
||||||
int index;
|
uint32_t index;
|
||||||
StrName(): index(-1) {}
|
StrName(): index(-1) {}
|
||||||
StrName(int index): index(index) {}
|
StrName(int index): index(index) {}
|
||||||
StrName(const char* s): index(get(s).index) {}
|
StrName(const char* s): index(get(s).index) {}
|
||||||
|
22
src/vm.h
22
src/vm.h
@ -152,7 +152,9 @@ public:
|
|||||||
} else if(is_type(*callable, tp_function)){
|
} else if(is_type(*callable, tp_function)){
|
||||||
const pkpy::Function& fn = PyFunction_AS_C(*callable);
|
const pkpy::Function& fn = PyFunction_AS_C(*callable);
|
||||||
auto locals = pkpy::make_shared<pkpy::NameDict>(
|
auto locals = pkpy::make_shared<pkpy::NameDict>(
|
||||||
fn.code->ideal_locals_capacity, kLocalsLoadFactor
|
fn.code->perfect_locals_capacity,
|
||||||
|
kLocalsLoadFactor,
|
||||||
|
fn.code->perfect_hash_seed
|
||||||
);
|
);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -184,7 +186,7 @@ public:
|
|||||||
for(int i=0; i<kwargs.size(); i+=2){
|
for(int i=0; i<kwargs.size(); i+=2){
|
||||||
const Str& key = PyStr_AS_C(kwargs[i]);
|
const Str& key = PyStr_AS_C(kwargs[i]);
|
||||||
if(!fn.kwargs.contains(key)){
|
if(!fn.kwargs.contains(key)){
|
||||||
TypeError(key.escape(true) + " is an invalid keyword argument for " + fn.name + "()");
|
TypeError(key.escape(true) + " is an invalid keyword argument for " + fn.name.str() + "()");
|
||||||
}
|
}
|
||||||
locals->emplace(key, kwargs[i+1]);
|
locals->emplace(key, kwargs[i+1]);
|
||||||
}
|
}
|
||||||
@ -647,6 +649,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
post_init();
|
post_init();
|
||||||
|
for(auto it = _types.begin(); it != _types.end(); ++it){
|
||||||
|
it->second->attr()._try_perfect_rehash();
|
||||||
|
}
|
||||||
|
builtins->attr()._try_perfect_rehash();
|
||||||
}
|
}
|
||||||
|
|
||||||
void post_init();
|
void post_init();
|
||||||
@ -727,6 +733,7 @@ public:
|
|||||||
PyVar type = new_type_object(mod, T::_name(), _t(tp_object));
|
PyVar type = new_type_object(mod, T::_name(), _t(tp_object));
|
||||||
if(OBJ_NAME(mod) != T::_mod()) UNREACHABLE();
|
if(OBJ_NAME(mod) != T::_mod()) UNREACHABLE();
|
||||||
T::_register(this, mod, type);
|
T::_register(this, mod, type);
|
||||||
|
type->attr()._try_perfect_rehash();
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,12 +874,11 @@ PyVar pkpy::NativeFunc::operator()(VM* vm, pkpy::Args& args) const{
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CodeObject::optimize(VM* vm){
|
void CodeObject::optimize(VM* vm){
|
||||||
int n = 0;
|
std::vector<StrName> keys;
|
||||||
for(auto& p: names) if(p.second == NAME_LOCAL) n++;
|
for(auto& p: names) if(p.second == NAME_LOCAL) keys.push_back(p.first);
|
||||||
// 0->2, 1->2, 2->4, 3->4, 4->8, 5->8, 6->16
|
uint32_t base_n = (uint32_t)(keys.size() / kLocalsLoadFactor + 0.5);
|
||||||
int base_n = (int)(n / kLocalsLoadFactor + 0.5);
|
perfect_locals_capacity = pkpy::find_next_prime(base_n);
|
||||||
ideal_locals_capacity = 2;
|
perfect_hash_seed = pkpy::find_perfect_hash_seed(perfect_locals_capacity, keys);
|
||||||
while(ideal_locals_capacity < base_n) ideal_locals_capacity *= 2;
|
|
||||||
|
|
||||||
for(int i=1; i<codes.size(); i++){
|
for(int i=1; i<codes.size(); i++){
|
||||||
if(codes[i].op == OP_UNARY_NEGATIVE && codes[i-1].op == OP_LOAD_CONST){
|
if(codes[i].op == OP_UNARY_NEGATIVE && codes[i-1].op == OP_LOAD_CONST){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user