Commit 816413e4 authored by Damien George's avatar Damien George
Browse files

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
parent fc710169
...@@ -525,8 +525,11 @@ struct _mp_obj_type_t { ...@@ -525,8 +525,11 @@ struct _mp_obj_type_t {
// One of disjoint protocols (interfaces), like mp_stream_p_t, etc. // One of disjoint protocols (interfaces), like mp_stream_p_t, etc.
const void *protocol; const void *protocol;
// A tuple containing all the base types of this type. // A pointer to the parents of this type:
struct _mp_obj_tuple_t *bases_tuple; // - 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. // A dict mapping qstrs to objects local methods/constants/etc.
struct _mp_obj_dict_t *locals_dict; struct _mp_obj_dict_t *locals_dict;
......
...@@ -577,8 +577,6 @@ const mp_obj_type_t mp_type_dict = { ...@@ -577,8 +577,6 @@ const mp_obj_type_t mp_type_dict = {
}; };
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT #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 = { const mp_obj_type_t mp_type_ordereddict = {
{ &mp_type_type }, { &mp_type_type },
.name = MP_QSTR_OrderedDict, .name = MP_QSTR_OrderedDict,
...@@ -588,7 +586,7 @@ const mp_obj_type_t mp_type_ordereddict = { ...@@ -588,7 +586,7 @@ const mp_obj_type_t mp_type_ordereddict = {
.binary_op = dict_binary_op, .binary_op = dict_binary_op,
.subscr = dict_subscr, .subscr = dict_subscr,
.getiter = dict_getiter, .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, .locals_dict = (mp_obj_dict_t*)&dict_locals_dict,
}; };
#endif #endif
......
...@@ -197,9 +197,6 @@ const mp_obj_type_t mp_type_BaseException = { ...@@ -197,9 +197,6 @@ const mp_obj_type_t mp_type_BaseException = {
.locals_dict = (mp_obj_dict_t*)&exc_locals_dict, .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) \ #define MP_DEFINE_EXCEPTION(exc_name, base_name) \
const mp_obj_type_t mp_type_ ## exc_name = { \ const mp_obj_type_t mp_type_ ## exc_name = { \
{ &mp_type_type }, \ { &mp_type_type }, \
...@@ -207,23 +204,20 @@ const mp_obj_type_t mp_type_ ## exc_name = { \ ...@@ -207,23 +204,20 @@ const mp_obj_type_t mp_type_ ## exc_name = { \
.print = mp_obj_exception_print, \ .print = mp_obj_exception_print, \
.make_new = mp_obj_exception_make_new, \ .make_new = mp_obj_exception_make_new, \
.attr = exception_attr, \ .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: // List of all exceptions, arranged as in the table at:
// http://docs.python.org/3/library/exceptions.html // http://docs.python.org/3/library/exceptions.html
MP_DEFINE_EXCEPTION_BASE(BaseException)
MP_DEFINE_EXCEPTION(SystemExit, BaseException) MP_DEFINE_EXCEPTION(SystemExit, BaseException)
MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException) MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException)
MP_DEFINE_EXCEPTION(GeneratorExit, BaseException) MP_DEFINE_EXCEPTION(GeneratorExit, BaseException)
MP_DEFINE_EXCEPTION(Exception, BaseException) MP_DEFINE_EXCEPTION(Exception, BaseException)
MP_DEFINE_EXCEPTION_BASE(Exception)
#if MICROPY_PY_ASYNC_AWAIT #if MICROPY_PY_ASYNC_AWAIT
MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception) MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception)
#endif #endif
MP_DEFINE_EXCEPTION(StopIteration, Exception) MP_DEFINE_EXCEPTION(StopIteration, Exception)
MP_DEFINE_EXCEPTION(ArithmeticError, Exception) MP_DEFINE_EXCEPTION(ArithmeticError, Exception)
MP_DEFINE_EXCEPTION_BASE(ArithmeticError)
//MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError) //MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError)
MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError) MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError)
MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError) MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError)
...@@ -235,18 +229,15 @@ MP_DEFINE_EXCEPTION(Exception, BaseException) ...@@ -235,18 +229,15 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
MP_DEFINE_EXCEPTION(ImportError, Exception) MP_DEFINE_EXCEPTION(ImportError, Exception)
//MP_DEFINE_EXCEPTION(IOError, Exception) use OSError instead //MP_DEFINE_EXCEPTION(IOError, Exception) use OSError instead
MP_DEFINE_EXCEPTION(LookupError, Exception) MP_DEFINE_EXCEPTION(LookupError, Exception)
MP_DEFINE_EXCEPTION_BASE(LookupError)
MP_DEFINE_EXCEPTION(IndexError, LookupError) MP_DEFINE_EXCEPTION(IndexError, LookupError)
MP_DEFINE_EXCEPTION(KeyError, LookupError) MP_DEFINE_EXCEPTION(KeyError, LookupError)
MP_DEFINE_EXCEPTION(MemoryError, Exception) MP_DEFINE_EXCEPTION(MemoryError, Exception)
MP_DEFINE_EXCEPTION(NameError, Exception) MP_DEFINE_EXCEPTION(NameError, Exception)
/* /*
MP_DEFINE_EXCEPTION_BASE(NameError)
MP_DEFINE_EXCEPTION(UnboundLocalError, NameError) MP_DEFINE_EXCEPTION(UnboundLocalError, NameError)
*/ */
MP_DEFINE_EXCEPTION(OSError, Exception) MP_DEFINE_EXCEPTION(OSError, Exception)
#if MICROPY_PY_BUILTINS_TIMEOUTERROR #if MICROPY_PY_BUILTINS_TIMEOUTERROR
MP_DEFINE_EXCEPTION_BASE(OSError)
MP_DEFINE_EXCEPTION(TimeoutError, OSError) MP_DEFINE_EXCEPTION(TimeoutError, OSError)
#endif #endif
/* /*
...@@ -267,30 +258,24 @@ MP_DEFINE_EXCEPTION(Exception, BaseException) ...@@ -267,30 +258,24 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
MP_DEFINE_EXCEPTION(ReferenceError, Exception) MP_DEFINE_EXCEPTION(ReferenceError, Exception)
*/ */
MP_DEFINE_EXCEPTION(RuntimeError, Exception) MP_DEFINE_EXCEPTION(RuntimeError, Exception)
MP_DEFINE_EXCEPTION_BASE(RuntimeError)
MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError) MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError)
MP_DEFINE_EXCEPTION(SyntaxError, Exception) MP_DEFINE_EXCEPTION(SyntaxError, Exception)
MP_DEFINE_EXCEPTION_BASE(SyntaxError)
MP_DEFINE_EXCEPTION(IndentationError, SyntaxError) MP_DEFINE_EXCEPTION(IndentationError, SyntaxError)
/* /*
MP_DEFINE_EXCEPTION_BASE(IndentationError)
MP_DEFINE_EXCEPTION(TabError, IndentationError) MP_DEFINE_EXCEPTION(TabError, IndentationError)
*/ */
//MP_DEFINE_EXCEPTION(SystemError, Exception) //MP_DEFINE_EXCEPTION(SystemError, Exception)
MP_DEFINE_EXCEPTION(TypeError, Exception) MP_DEFINE_EXCEPTION(TypeError, Exception)
#if MICROPY_EMIT_NATIVE #if MICROPY_EMIT_NATIVE
MP_DEFINE_EXCEPTION_BASE(TypeError)
MP_DEFINE_EXCEPTION(ViperTypeError, TypeError) MP_DEFINE_EXCEPTION(ViperTypeError, TypeError)
#endif #endif
MP_DEFINE_EXCEPTION(ValueError, Exception) MP_DEFINE_EXCEPTION(ValueError, Exception)
#if MICROPY_PY_BUILTINS_STR_UNICODE #if MICROPY_PY_BUILTINS_STR_UNICODE
MP_DEFINE_EXCEPTION_BASE(ValueError)
MP_DEFINE_EXCEPTION(UnicodeError, ValueError) MP_DEFINE_EXCEPTION(UnicodeError, ValueError)
//TODO: Implement more UnicodeError subclasses which take arguments //TODO: Implement more UnicodeError subclasses which take arguments
#endif #endif
/* /*
MP_DEFINE_EXCEPTION(Warning, Exception) MP_DEFINE_EXCEPTION(Warning, Exception)
MP_DEFINE_EXCEPTION_BASE(Warning)
MP_DEFINE_EXCEPTION(DeprecationWarning, Warning) MP_DEFINE_EXCEPTION(DeprecationWarning, Warning)
MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning) MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning)
MP_DEFINE_EXCEPTION(RuntimeWarning, 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, ...@@ -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); 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) { 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); 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)); 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 ...@@ -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.attr = namedtuple_attr;
o->base.subscr = mp_obj_tuple_subscr; o->base.subscr = mp_obj_tuple_subscr;
o->base.getiter = mp_obj_tuple_getiter; 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; o->n_fields = n_fields;
for (size_t i = 0; i < n_fields; i++) { for (size_t i = 0; i < n_fields; i++) {
o->fields[i] = mp_obj_str_get_qstr(fields[i]); o->fields[i] = mp_obj_str_get_qstr(fields[i]);
......
...@@ -57,26 +57,34 @@ STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) ...@@ -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) { 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; int count = 0;
for (size_t i = 0; i < len; i++) { for (;;) {
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type)); if (type == &mp_type_object) {
const mp_obj_type_t *bt = (const mp_obj_type_t *)MP_OBJ_TO_PTR(items[i]); // Not a "real" type, end search here.
if (bt == &mp_type_object) { return count;
// Not a "real" type } else if (mp_obj_is_native_type(type)) {
continue; // Native types don't have parents (at least not from our perspective) so end.
} *last_native_base = type;
if (mp_obj_is_native_type(bt)) { return count + 1;
*last_native_base = bt; } else if (type->parent == NULL) {
count++; // 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 { } 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 // TODO
...@@ -160,32 +168,31 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_ ...@@ -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 // attribute not found, keep searching base classes
// for a const struct, this entry might be NULL if (type->parent == NULL) {
if (type->bases_tuple == NULL) {
return;
}
size_t len = type->bases_tuple->len;
mp_obj_t *items = type->bases_tuple->items;
if (len == 0) {
return; return;
} } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
for (size_t i = 0; i < len - 1; i++) { const mp_obj_tuple_t *parent_tuple = type->parent;
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type)); const mp_obj_t *item = parent_tuple->items;
mp_obj_type_t *bt = (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]); const mp_obj_t *top = item + parent_tuple->len - 1;
if (bt == &mp_type_object) { for (; item < top; ++item) {
// Not a "real" type assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
continue; mp_obj_type_t *bt = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
} if (bt == &mp_type_object) {
mp_obj_class_lookup(lookup, bt); // Not a "real" type
if (lookup->dest[0] != MP_OBJ_NULL) { continue;
return; }
mp_obj_class_lookup(lookup, bt);
if (lookup->dest[0] != MP_OBJ_NULL) {
return;
}
} }
}
// search last base (simple tail recursion elimination) // search last base (simple tail recursion elimination)
assert(MP_OBJ_IS_TYPE(items[len - 1], &mp_type_type)); assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
type = (mp_obj_type_t*)MP_OBJ_TO_PTR(items[len - 1]); type = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
} else {
type = type->parent;
}
if (type == &mp_type_object) { if (type == &mp_type_object) {
// Not a "real" type // Not a "real" type
return; return;
...@@ -946,14 +953,21 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) ...@@ -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->getiter = instance_getiter;
//o->iternext = ; not implemented //o->iternext = ; not implemented
o->buffer_p.get_buffer = instance_get_buffer; 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) { 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; 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); o->locals_dict = MP_OBJ_TO_PTR(locals_dict);
const mp_obj_type_t *native_base; 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) { ...@@ -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); 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 = { struct class_lookup_data lookup = {
.obj = MP_OBJ_TO_PTR(self->obj), .obj = MP_OBJ_TO_PTR(self->obj),
.attr = attr, .attr = attr,
...@@ -1029,13 +1036,27 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { ...@@ -1029,13 +1036,27 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
.dest = dest, .dest = dest,
.is_type = false, .is_type = false,
}; };
for (size_t i = 0; i < len; i++) {
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type)); if (type->parent == NULL) {
mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i])); // 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) { if (dest[0] != MP_OBJ_NULL) {
return; return;
} }
} }
mp_obj_class_lookup(&lookup, &mp_type_object); 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) { ...@@ -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); const mp_obj_type_t *self = MP_OBJ_TO_PTR(object);
// for a const struct, this entry might be NULL if (self->parent == NULL) {
if (self->bases_tuple == NULL) { // type has no parents
return false;
}
// 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; return false;
} } else if (((mp_obj_base_t*)self->parent)->type == &mp_type_tuple) {
// get the base objects (they should be type objects)
// iterate through the base objects const mp_obj_tuple_t *parent_tuple = self->parent;
for (size_t i = 0; i < len - 1; i++) { const mp_obj_t *item = parent_tuple->items;
if (mp_obj_is_subclass_fast(items[i], classinfo)) { const mp_obj_t *top = item + parent_tuple->len - 1;
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) // search last base (simple tail recursion elimination)
object = items[len - 1]; object = *item;
} else {
// type has 1 parent
object = MP_OBJ_FROM_PTR(self->parent);
}
} }
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment