py: Optimise types for common case where type has a single parent type.
The common cases for inheritance are 0 or 1 parent types, for both built-in types (eg built-in exceptions) as well as user defined types. So it makes sense to optimise the case of 1 parent type by storing just the type and not a tuple of 1 value (that value being the single parent type). This patch makes such an optimisation. Even though there is a bit more code to handle the two cases (either a single type or a tuple with 2 or more values) it helps reduce overall code size because it eliminates the need to create a static tuple to hold single parents (eg for the built-in exceptions). It also helps reduce RAM usage for user defined types that only derive from a single parent. Changes in code size (in bytes) due to this patch: bare-arm: -16 minimal (x86): -176 unix (x86-64): -320 unix nanbox: -384 stmhal: -64 cc3200: -32 esp8266: -108
This commit is contained in:
parent
fc710169b7
commit
816413e4b2
7
py/obj.h
7
py/obj.h
@ -525,8 +525,11 @@ struct _mp_obj_type_t {
|
||||
// One of disjoint protocols (interfaces), like mp_stream_p_t, etc.
|
||||
const void *protocol;
|
||||
|
||||
// A tuple containing all the base types of this type.
|
||||
struct _mp_obj_tuple_t *bases_tuple;
|
||||
// A pointer to the parents of this type:
|
||||
// - 0 parents: pointer is NULL (object is implicitly the single parent)
|
||||
// - 1 parent: a pointer to the type of that parent
|
||||
// - 2 or more parents: pointer to a tuple object containing the parent types
|
||||
const void *parent;
|
||||
|
||||
// A dict mapping qstrs to objects local methods/constants/etc.
|
||||
struct _mp_obj_dict_t *locals_dict;
|
||||
|
@ -577,8 +577,6 @@ const mp_obj_type_t mp_type_dict = {
|
||||
};
|
||||
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
STATIC const mp_rom_obj_tuple_t ordereddict_base_tuple = {{&mp_type_tuple}, 1, {MP_ROM_PTR(&mp_type_dict)}};
|
||||
|
||||
const mp_obj_type_t mp_type_ordereddict = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_OrderedDict,
|
||||
@ -588,7 +586,7 @@ const mp_obj_type_t mp_type_ordereddict = {
|
||||
.binary_op = dict_binary_op,
|
||||
.subscr = dict_subscr,
|
||||
.getiter = dict_getiter,
|
||||
.bases_tuple = (mp_obj_tuple_t*)(mp_rom_obj_tuple_t*)&ordereddict_base_tuple,
|
||||
.parent = &mp_type_dict,
|
||||
.locals_dict = (mp_obj_dict_t*)&dict_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
@ -197,9 +197,6 @@ const mp_obj_type_t mp_type_BaseException = {
|
||||
.locals_dict = (mp_obj_dict_t*)&exc_locals_dict,
|
||||
};
|
||||
|
||||
#define MP_DEFINE_EXCEPTION_BASE(base_name) \
|
||||
STATIC const mp_rom_obj_tuple_t mp_type_ ## base_name ## _base_tuple = {{&mp_type_tuple}, 1, {MP_ROM_PTR(&mp_type_ ## base_name)}};\
|
||||
|
||||
#define MP_DEFINE_EXCEPTION(exc_name, base_name) \
|
||||
const mp_obj_type_t mp_type_ ## exc_name = { \
|
||||
{ &mp_type_type }, \
|
||||
@ -207,23 +204,20 @@ const mp_obj_type_t mp_type_ ## exc_name = { \
|
||||
.print = mp_obj_exception_print, \
|
||||
.make_new = mp_obj_exception_make_new, \
|
||||
.attr = exception_attr, \
|
||||
.bases_tuple = (mp_obj_tuple_t*)(mp_rom_obj_tuple_t*)&mp_type_ ## base_name ## _base_tuple, \
|
||||
.parent = &mp_type_ ## base_name, \
|
||||
};
|
||||
|
||||
// List of all exceptions, arranged as in the table at:
|
||||
// http://docs.python.org/3/library/exceptions.html
|
||||
MP_DEFINE_EXCEPTION_BASE(BaseException)
|
||||
MP_DEFINE_EXCEPTION(SystemExit, BaseException)
|
||||
MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException)
|
||||
MP_DEFINE_EXCEPTION(GeneratorExit, BaseException)
|
||||
MP_DEFINE_EXCEPTION(Exception, BaseException)
|
||||
MP_DEFINE_EXCEPTION_BASE(Exception)
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception)
|
||||
#endif
|
||||
MP_DEFINE_EXCEPTION(StopIteration, Exception)
|
||||
MP_DEFINE_EXCEPTION(ArithmeticError, Exception)
|
||||
MP_DEFINE_EXCEPTION_BASE(ArithmeticError)
|
||||
//MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError)
|
||||
MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError)
|
||||
MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError)
|
||||
@ -235,18 +229,15 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
|
||||
MP_DEFINE_EXCEPTION(ImportError, Exception)
|
||||
//MP_DEFINE_EXCEPTION(IOError, Exception) use OSError instead
|
||||
MP_DEFINE_EXCEPTION(LookupError, Exception)
|
||||
MP_DEFINE_EXCEPTION_BASE(LookupError)
|
||||
MP_DEFINE_EXCEPTION(IndexError, LookupError)
|
||||
MP_DEFINE_EXCEPTION(KeyError, LookupError)
|
||||
MP_DEFINE_EXCEPTION(MemoryError, Exception)
|
||||
MP_DEFINE_EXCEPTION(NameError, Exception)
|
||||
/*
|
||||
MP_DEFINE_EXCEPTION_BASE(NameError)
|
||||
MP_DEFINE_EXCEPTION(UnboundLocalError, NameError)
|
||||
*/
|
||||
MP_DEFINE_EXCEPTION(OSError, Exception)
|
||||
#if MICROPY_PY_BUILTINS_TIMEOUTERROR
|
||||
MP_DEFINE_EXCEPTION_BASE(OSError)
|
||||
MP_DEFINE_EXCEPTION(TimeoutError, OSError)
|
||||
#endif
|
||||
/*
|
||||
@ -267,30 +258,24 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
|
||||
MP_DEFINE_EXCEPTION(ReferenceError, Exception)
|
||||
*/
|
||||
MP_DEFINE_EXCEPTION(RuntimeError, Exception)
|
||||
MP_DEFINE_EXCEPTION_BASE(RuntimeError)
|
||||
MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError)
|
||||
MP_DEFINE_EXCEPTION(SyntaxError, Exception)
|
||||
MP_DEFINE_EXCEPTION_BASE(SyntaxError)
|
||||
MP_DEFINE_EXCEPTION(IndentationError, SyntaxError)
|
||||
/*
|
||||
MP_DEFINE_EXCEPTION_BASE(IndentationError)
|
||||
MP_DEFINE_EXCEPTION(TabError, IndentationError)
|
||||
*/
|
||||
//MP_DEFINE_EXCEPTION(SystemError, Exception)
|
||||
MP_DEFINE_EXCEPTION(TypeError, Exception)
|
||||
#if MICROPY_EMIT_NATIVE
|
||||
MP_DEFINE_EXCEPTION_BASE(TypeError)
|
||||
MP_DEFINE_EXCEPTION(ViperTypeError, TypeError)
|
||||
#endif
|
||||
MP_DEFINE_EXCEPTION(ValueError, Exception)
|
||||
#if MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
MP_DEFINE_EXCEPTION_BASE(ValueError)
|
||||
MP_DEFINE_EXCEPTION(UnicodeError, ValueError)
|
||||
//TODO: Implement more UnicodeError subclasses which take arguments
|
||||
#endif
|
||||
/*
|
||||
MP_DEFINE_EXCEPTION(Warning, Exception)
|
||||
MP_DEFINE_EXCEPTION_BASE(Warning)
|
||||
MP_DEFINE_EXCEPTION(DeprecationWarning, Warning)
|
||||
MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning)
|
||||
MP_DEFINE_EXCEPTION(RuntimeWarning, Warning)
|
||||
|
@ -134,8 +134,6 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args,
|
||||
return MP_OBJ_FROM_PTR(tuple);
|
||||
}
|
||||
|
||||
STATIC const mp_rom_obj_tuple_t namedtuple_base_tuple = {{&mp_type_tuple}, 1, {MP_ROM_PTR(&mp_type_tuple)}};
|
||||
|
||||
STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) {
|
||||
mp_obj_namedtuple_type_t *o = m_new_obj_var(mp_obj_namedtuple_type_t, qstr, n_fields);
|
||||
memset(&o->base, 0, sizeof(o->base));
|
||||
@ -148,7 +146,7 @@ STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t
|
||||
o->base.attr = namedtuple_attr;
|
||||
o->base.subscr = mp_obj_tuple_subscr;
|
||||
o->base.getiter = mp_obj_tuple_getiter;
|
||||
o->base.bases_tuple = (mp_obj_tuple_t*)(mp_rom_obj_tuple_t*)&namedtuple_base_tuple;
|
||||
o->base.parent = &mp_type_tuple;
|
||||
o->n_fields = n_fields;
|
||||
for (size_t i = 0; i < n_fields; i++) {
|
||||
o->fields[i] = mp_obj_str_get_qstr(fields[i]);
|
||||
|
164
py/objtype.c
164
py/objtype.c
@ -57,26 +57,34 @@ STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs)
|
||||
}
|
||||
|
||||
STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_type_t **last_native_base) {
|
||||
size_t len = type->bases_tuple->len;
|
||||
mp_obj_t *items = type->bases_tuple->items;
|
||||
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
|
||||
const mp_obj_type_t *bt = (const mp_obj_type_t *)MP_OBJ_TO_PTR(items[i]);
|
||||
if (bt == &mp_type_object) {
|
||||
// Not a "real" type
|
||||
continue;
|
||||
}
|
||||
if (mp_obj_is_native_type(bt)) {
|
||||
*last_native_base = bt;
|
||||
count++;
|
||||
for (;;) {
|
||||
if (type == &mp_type_object) {
|
||||
// Not a "real" type, end search here.
|
||||
return count;
|
||||
} else if (mp_obj_is_native_type(type)) {
|
||||
// Native types don't have parents (at least not from our perspective) so end.
|
||||
*last_native_base = type;
|
||||
return count + 1;
|
||||
} else if (type->parent == NULL) {
|
||||
// No parents so end search here.
|
||||
return count;
|
||||
} else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
|
||||
// Multiple parents, search through them all recursively.
|
||||
const mp_obj_tuple_t *parent_tuple = type->parent;
|
||||
const mp_obj_t *item = parent_tuple->items;
|
||||
const mp_obj_t *top = item + parent_tuple->len;
|
||||
for (; item < top; ++item) {
|
||||
assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
|
||||
const mp_obj_type_t *bt = (const mp_obj_type_t *)MP_OBJ_TO_PTR(*item);
|
||||
count += instance_count_native_bases(bt, last_native_base);
|
||||
}
|
||||
return count;
|
||||
} else {
|
||||
count += instance_count_native_bases(bt, last_native_base);
|
||||
// A single parent, use iteration to continue the search.
|
||||
type = type->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// TODO
|
||||
@ -160,32 +168,31 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_
|
||||
|
||||
// attribute not found, keep searching base classes
|
||||
|
||||
// for a const struct, this entry might be NULL
|
||||
if (type->bases_tuple == NULL) {
|
||||
if (type->parent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = type->bases_tuple->len;
|
||||
mp_obj_t *items = type->bases_tuple->items;
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < len - 1; i++) {
|
||||
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
|
||||
mp_obj_type_t *bt = (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]);
|
||||
if (bt == &mp_type_object) {
|
||||
// Not a "real" type
|
||||
continue;
|
||||
} else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
|
||||
const mp_obj_tuple_t *parent_tuple = type->parent;
|
||||
const mp_obj_t *item = parent_tuple->items;
|
||||
const mp_obj_t *top = item + parent_tuple->len - 1;
|
||||
for (; item < top; ++item) {
|
||||
assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
|
||||
mp_obj_type_t *bt = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
|
||||
if (bt == &mp_type_object) {
|
||||
// Not a "real" type
|
||||
continue;
|
||||
}
|
||||
mp_obj_class_lookup(lookup, bt);
|
||||
if (lookup->dest[0] != MP_OBJ_NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mp_obj_class_lookup(lookup, bt);
|
||||
if (lookup->dest[0] != MP_OBJ_NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// search last base (simple tail recursion elimination)
|
||||
assert(MP_OBJ_IS_TYPE(items[len - 1], &mp_type_type));
|
||||
type = (mp_obj_type_t*)MP_OBJ_TO_PTR(items[len - 1]);
|
||||
// search last base (simple tail recursion elimination)
|
||||
assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
|
||||
type = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
|
||||
} else {
|
||||
type = type->parent;
|
||||
}
|
||||
if (type == &mp_type_object) {
|
||||
// Not a "real" type
|
||||
return;
|
||||
@ -946,14 +953,21 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
|
||||
o->getiter = instance_getiter;
|
||||
//o->iternext = ; not implemented
|
||||
o->buffer_p.get_buffer = instance_get_buffer;
|
||||
// Inherit protocol from a base class. This allows to define an
|
||||
// abstract base class which would translate C-level protocol to
|
||||
// Python method calls, and any subclass inheriting from it will
|
||||
// support this feature.
|
||||
|
||||
if (len > 0) {
|
||||
// Inherit protocol from a base class. This allows to define an
|
||||
// abstract base class which would translate C-level protocol to
|
||||
// Python method calls, and any subclass inheriting from it will
|
||||
// support this feature.
|
||||
o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(items[0]))->protocol;
|
||||
|
||||
if (len >= 2) {
|
||||
o->parent = MP_OBJ_TO_PTR(bases_tuple);
|
||||
} else {
|
||||
o->parent = MP_OBJ_TO_PTR(items[0]);
|
||||
}
|
||||
}
|
||||
o->bases_tuple = MP_OBJ_TO_PTR(bases_tuple);
|
||||
|
||||
o->locals_dict = MP_OBJ_TO_PTR(locals_dict);
|
||||
|
||||
const mp_obj_type_t *native_base;
|
||||
@ -1015,13 +1029,6 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
|
||||
mp_obj_type_t *type = MP_OBJ_TO_PTR(self->type);
|
||||
|
||||
// for a const struct, this entry might be NULL
|
||||
if (type->bases_tuple == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = type->bases_tuple->len;
|
||||
mp_obj_t *items = type->bases_tuple->items;
|
||||
struct class_lookup_data lookup = {
|
||||
.obj = MP_OBJ_TO_PTR(self->obj),
|
||||
.attr = attr,
|
||||
@ -1029,13 +1036,27 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
.dest = dest,
|
||||
.is_type = false,
|
||||
};
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
|
||||
mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]));
|
||||
|
||||
if (type->parent == NULL) {
|
||||
// no parents, do nothing
|
||||
} else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
|
||||
const mp_obj_tuple_t *parent_tuple = type->parent;
|
||||
size_t len = parent_tuple->len;
|
||||
const mp_obj_t *items = parent_tuple->items;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
|
||||
mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]));
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mp_obj_class_lookup(&lookup, type->parent);
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_class_lookup(&lookup, &mp_type_object);
|
||||
}
|
||||
|
||||
@ -1073,27 +1094,28 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) {
|
||||
|
||||
const mp_obj_type_t *self = MP_OBJ_TO_PTR(object);
|
||||
|
||||
// for a const struct, this entry might be NULL
|
||||
if (self->bases_tuple == NULL) {
|
||||
if (self->parent == NULL) {
|
||||
// type has no parents
|
||||
return false;
|
||||
}
|
||||
} else if (((mp_obj_base_t*)self->parent)->type == &mp_type_tuple) {
|
||||
// get the base objects (they should be type objects)
|
||||
const mp_obj_tuple_t *parent_tuple = self->parent;
|
||||
const mp_obj_t *item = parent_tuple->items;
|
||||
const mp_obj_t *top = item + parent_tuple->len - 1;
|
||||
|
||||
// get the base objects (they should be type objects)
|
||||
size_t len = self->bases_tuple->len;
|
||||
mp_obj_t *items = self->bases_tuple->items;
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate through the base objects
|
||||
for (size_t i = 0; i < len - 1; i++) {
|
||||
if (mp_obj_is_subclass_fast(items[i], classinfo)) {
|
||||
return true;
|
||||
// iterate through the base objects
|
||||
for (; item < top; ++item) {
|
||||
if (mp_obj_is_subclass_fast(*item, classinfo)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search last base (simple tail recursion elimination)
|
||||
object = items[len - 1];
|
||||
// search last base (simple tail recursion elimination)
|
||||
object = *item;
|
||||
} else {
|
||||
// type has 1 parent
|
||||
object = MP_OBJ_FROM_PTR(self->parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user