Skip to content

Commit 816413e

Browse files
committed
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
1 parent fc71016 commit 816413e

5 files changed

Lines changed: 102 additions & 96 deletions

File tree

py/obj.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,11 @@ struct _mp_obj_type_t {
525525
// One of disjoint protocols (interfaces), like mp_stream_p_t, etc.
526526
const void *protocol;
527527

528-
// A tuple containing all the base types of this type.
529-
struct _mp_obj_tuple_t *bases_tuple;
528+
// A pointer to the parents of this type:
529+
// - 0 parents: pointer is NULL (object is implicitly the single parent)
530+
// - 1 parent: a pointer to the type of that parent
531+
// - 2 or more parents: pointer to a tuple object containing the parent types
532+
const void *parent;
530533

531534
// A dict mapping qstrs to objects local methods/constants/etc.
532535
struct _mp_obj_dict_t *locals_dict;

py/objdict.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,6 @@ const mp_obj_type_t mp_type_dict = {
577577
};
578578

579579
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
580-
STATIC const mp_rom_obj_tuple_t ordereddict_base_tuple = {{&mp_type_tuple}, 1, {MP_ROM_PTR(&mp_type_dict)}};
581-
582580
const mp_obj_type_t mp_type_ordereddict = {
583581
{ &mp_type_type },
584582
.name = MP_QSTR_OrderedDict,
@@ -588,7 +586,7 @@ const mp_obj_type_t mp_type_ordereddict = {
588586
.binary_op = dict_binary_op,
589587
.subscr = dict_subscr,
590588
.getiter = dict_getiter,
591-
.bases_tuple = (mp_obj_tuple_t*)(mp_rom_obj_tuple_t*)&ordereddict_base_tuple,
589+
.parent = &mp_type_dict,
592590
.locals_dict = (mp_obj_dict_t*)&dict_locals_dict,
593591
};
594592
#endif

py/objexcept.c

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -197,33 +197,27 @@ const mp_obj_type_t mp_type_BaseException = {
197197
.locals_dict = (mp_obj_dict_t*)&exc_locals_dict,
198198
};
199199

200-
#define MP_DEFINE_EXCEPTION_BASE(base_name) \
201-
STATIC const mp_rom_obj_tuple_t mp_type_ ## base_name ## _base_tuple = {{&mp_type_tuple}, 1, {MP_ROM_PTR(&mp_type_ ## base_name)}};\
202-
203200
#define MP_DEFINE_EXCEPTION(exc_name, base_name) \
204201
const mp_obj_type_t mp_type_ ## exc_name = { \
205202
{ &mp_type_type }, \
206203
.name = MP_QSTR_ ## exc_name, \
207204
.print = mp_obj_exception_print, \
208205
.make_new = mp_obj_exception_make_new, \
209206
.attr = exception_attr, \
210-
.bases_tuple = (mp_obj_tuple_t*)(mp_rom_obj_tuple_t*)&mp_type_ ## base_name ## _base_tuple, \
207+
.parent = &mp_type_ ## base_name, \
211208
};
212209

213210
// List of all exceptions, arranged as in the table at:
214211
// http://docs.python.org/3/library/exceptions.html
215-
MP_DEFINE_EXCEPTION_BASE(BaseException)
216212
MP_DEFINE_EXCEPTION(SystemExit, BaseException)
217213
MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException)
218214
MP_DEFINE_EXCEPTION(GeneratorExit, BaseException)
219215
MP_DEFINE_EXCEPTION(Exception, BaseException)
220-
MP_DEFINE_EXCEPTION_BASE(Exception)
221216
#if MICROPY_PY_ASYNC_AWAIT
222217
MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception)
223218
#endif
224219
MP_DEFINE_EXCEPTION(StopIteration, Exception)
225220
MP_DEFINE_EXCEPTION(ArithmeticError, Exception)
226-
MP_DEFINE_EXCEPTION_BASE(ArithmeticError)
227221
//MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError)
228222
MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError)
229223
MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError)
@@ -235,18 +229,15 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
235229
MP_DEFINE_EXCEPTION(ImportError, Exception)
236230
//MP_DEFINE_EXCEPTION(IOError, Exception) use OSError instead
237231
MP_DEFINE_EXCEPTION(LookupError, Exception)
238-
MP_DEFINE_EXCEPTION_BASE(LookupError)
239232
MP_DEFINE_EXCEPTION(IndexError, LookupError)
240233
MP_DEFINE_EXCEPTION(KeyError, LookupError)
241234
MP_DEFINE_EXCEPTION(MemoryError, Exception)
242235
MP_DEFINE_EXCEPTION(NameError, Exception)
243236
/*
244-
MP_DEFINE_EXCEPTION_BASE(NameError)
245237
MP_DEFINE_EXCEPTION(UnboundLocalError, NameError)
246238
*/
247239
MP_DEFINE_EXCEPTION(OSError, Exception)
248240
#if MICROPY_PY_BUILTINS_TIMEOUTERROR
249-
MP_DEFINE_EXCEPTION_BASE(OSError)
250241
MP_DEFINE_EXCEPTION(TimeoutError, OSError)
251242
#endif
252243
/*
@@ -267,30 +258,24 @@ MP_DEFINE_EXCEPTION(Exception, BaseException)
267258
MP_DEFINE_EXCEPTION(ReferenceError, Exception)
268259
*/
269260
MP_DEFINE_EXCEPTION(RuntimeError, Exception)
270-
MP_DEFINE_EXCEPTION_BASE(RuntimeError)
271261
MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError)
272262
MP_DEFINE_EXCEPTION(SyntaxError, Exception)
273-
MP_DEFINE_EXCEPTION_BASE(SyntaxError)
274263
MP_DEFINE_EXCEPTION(IndentationError, SyntaxError)
275264
/*
276-
MP_DEFINE_EXCEPTION_BASE(IndentationError)
277265
MP_DEFINE_EXCEPTION(TabError, IndentationError)
278266
*/
279267
//MP_DEFINE_EXCEPTION(SystemError, Exception)
280268
MP_DEFINE_EXCEPTION(TypeError, Exception)
281269
#if MICROPY_EMIT_NATIVE
282-
MP_DEFINE_EXCEPTION_BASE(TypeError)
283270
MP_DEFINE_EXCEPTION(ViperTypeError, TypeError)
284271
#endif
285272
MP_DEFINE_EXCEPTION(ValueError, Exception)
286273
#if MICROPY_PY_BUILTINS_STR_UNICODE
287-
MP_DEFINE_EXCEPTION_BASE(ValueError)
288274
MP_DEFINE_EXCEPTION(UnicodeError, ValueError)
289275
//TODO: Implement more UnicodeError subclasses which take arguments
290276
#endif
291277
/*
292278
MP_DEFINE_EXCEPTION(Warning, Exception)
293-
MP_DEFINE_EXCEPTION_BASE(Warning)
294279
MP_DEFINE_EXCEPTION(DeprecationWarning, Warning)
295280
MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning)
296281
MP_DEFINE_EXCEPTION(RuntimeWarning, Warning)

py/objnamedtuple.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args,
134134
return MP_OBJ_FROM_PTR(tuple);
135135
}
136136

137-
STATIC const mp_rom_obj_tuple_t namedtuple_base_tuple = {{&mp_type_tuple}, 1, {MP_ROM_PTR(&mp_type_tuple)}};
138-
139137
STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) {
140138
mp_obj_namedtuple_type_t *o = m_new_obj_var(mp_obj_namedtuple_type_t, qstr, n_fields);
141139
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
148146
o->base.attr = namedtuple_attr;
149147
o->base.subscr = mp_obj_tuple_subscr;
150148
o->base.getiter = mp_obj_tuple_getiter;
151-
o->base.bases_tuple = (mp_obj_tuple_t*)(mp_rom_obj_tuple_t*)&namedtuple_base_tuple;
149+
o->base.parent = &mp_type_tuple;
152150
o->n_fields = n_fields;
153151
for (size_t i = 0; i < n_fields; i++) {
154152
o->fields[i] = mp_obj_str_get_qstr(fields[i]);

py/objtype.c

Lines changed: 94 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -57,26 +57,34 @@ STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs)
5757
}
5858

5959
STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_type_t **last_native_base) {
60-
size_t len = type->bases_tuple->len;
61-
mp_obj_t *items = type->bases_tuple->items;
62-
6360
int count = 0;
64-
for (size_t i = 0; i < len; i++) {
65-
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
66-
const mp_obj_type_t *bt = (const mp_obj_type_t *)MP_OBJ_TO_PTR(items[i]);
67-
if (bt == &mp_type_object) {
68-
// Not a "real" type
69-
continue;
70-
}
71-
if (mp_obj_is_native_type(bt)) {
72-
*last_native_base = bt;
73-
count++;
61+
for (;;) {
62+
if (type == &mp_type_object) {
63+
// Not a "real" type, end search here.
64+
return count;
65+
} else if (mp_obj_is_native_type(type)) {
66+
// Native types don't have parents (at least not from our perspective) so end.
67+
*last_native_base = type;
68+
return count + 1;
69+
} else if (type->parent == NULL) {
70+
// No parents so end search here.
71+
return count;
72+
} else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
73+
// Multiple parents, search through them all recursively.
74+
const mp_obj_tuple_t *parent_tuple = type->parent;
75+
const mp_obj_t *item = parent_tuple->items;
76+
const mp_obj_t *top = item + parent_tuple->len;
77+
for (; item < top; ++item) {
78+
assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
79+
const mp_obj_type_t *bt = (const mp_obj_type_t *)MP_OBJ_TO_PTR(*item);
80+
count += instance_count_native_bases(bt, last_native_base);
81+
}
82+
return count;
7483
} else {
75-
count += instance_count_native_bases(bt, last_native_base);
84+
// A single parent, use iteration to continue the search.
85+
type = type->parent;
7686
}
7787
}
78-
79-
return count;
8088
}
8189

8290
// TODO
@@ -160,32 +168,31 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_
160168

161169
// attribute not found, keep searching base classes
162170

163-
// for a const struct, this entry might be NULL
164-
if (type->bases_tuple == NULL) {
165-
return;
166-
}
167-
168-
size_t len = type->bases_tuple->len;
169-
mp_obj_t *items = type->bases_tuple->items;
170-
if (len == 0) {
171+
if (type->parent == NULL) {
171172
return;
172-
}
173-
for (size_t i = 0; i < len - 1; i++) {
174-
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
175-
mp_obj_type_t *bt = (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]);
176-
if (bt == &mp_type_object) {
177-
// Not a "real" type
178-
continue;
179-
}
180-
mp_obj_class_lookup(lookup, bt);
181-
if (lookup->dest[0] != MP_OBJ_NULL) {
182-
return;
173+
} else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
174+
const mp_obj_tuple_t *parent_tuple = type->parent;
175+
const mp_obj_t *item = parent_tuple->items;
176+
const mp_obj_t *top = item + parent_tuple->len - 1;
177+
for (; item < top; ++item) {
178+
assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
179+
mp_obj_type_t *bt = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
180+
if (bt == &mp_type_object) {
181+
// Not a "real" type
182+
continue;
183+
}
184+
mp_obj_class_lookup(lookup, bt);
185+
if (lookup->dest[0] != MP_OBJ_NULL) {
186+
return;
187+
}
183188
}
184-
}
185189

186-
// search last base (simple tail recursion elimination)
187-
assert(MP_OBJ_IS_TYPE(items[len - 1], &mp_type_type));
188-
type = (mp_obj_type_t*)MP_OBJ_TO_PTR(items[len - 1]);
190+
// search last base (simple tail recursion elimination)
191+
assert(MP_OBJ_IS_TYPE(*item, &mp_type_type));
192+
type = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item);
193+
} else {
194+
type = type->parent;
195+
}
189196
if (type == &mp_type_object) {
190197
// Not a "real" type
191198
return;
@@ -946,14 +953,21 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
946953
o->getiter = instance_getiter;
947954
//o->iternext = ; not implemented
948955
o->buffer_p.get_buffer = instance_get_buffer;
949-
// Inherit protocol from a base class. This allows to define an
950-
// abstract base class which would translate C-level protocol to
951-
// Python method calls, and any subclass inheriting from it will
952-
// support this feature.
956+
953957
if (len > 0) {
958+
// Inherit protocol from a base class. This allows to define an
959+
// abstract base class which would translate C-level protocol to
960+
// Python method calls, and any subclass inheriting from it will
961+
// support this feature.
954962
o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(items[0]))->protocol;
963+
964+
if (len >= 2) {
965+
o->parent = MP_OBJ_TO_PTR(bases_tuple);
966+
} else {
967+
o->parent = MP_OBJ_TO_PTR(items[0]);
968+
}
955969
}
956-
o->bases_tuple = MP_OBJ_TO_PTR(bases_tuple);
970+
957971
o->locals_dict = MP_OBJ_TO_PTR(locals_dict);
958972

959973
const mp_obj_type_t *native_base;
@@ -1015,27 +1029,34 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
10151029

10161030
mp_obj_type_t *type = MP_OBJ_TO_PTR(self->type);
10171031

1018-
// for a const struct, this entry might be NULL
1019-
if (type->bases_tuple == NULL) {
1020-
return;
1021-
}
1022-
1023-
size_t len = type->bases_tuple->len;
1024-
mp_obj_t *items = type->bases_tuple->items;
10251032
struct class_lookup_data lookup = {
10261033
.obj = MP_OBJ_TO_PTR(self->obj),
10271034
.attr = attr,
10281035
.meth_offset = 0,
10291036
.dest = dest,
10301037
.is_type = false,
10311038
};
1032-
for (size_t i = 0; i < len; i++) {
1033-
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
1034-
mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]));
1039+
1040+
if (type->parent == NULL) {
1041+
// no parents, do nothing
1042+
} else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) {
1043+
const mp_obj_tuple_t *parent_tuple = type->parent;
1044+
size_t len = parent_tuple->len;
1045+
const mp_obj_t *items = parent_tuple->items;
1046+
for (size_t i = 0; i < len; i++) {
1047+
assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type));
1048+
mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i]));
1049+
if (dest[0] != MP_OBJ_NULL) {
1050+
return;
1051+
}
1052+
}
1053+
} else {
1054+
mp_obj_class_lookup(&lookup, type->parent);
10351055
if (dest[0] != MP_OBJ_NULL) {
10361056
return;
10371057
}
10381058
}
1059+
10391060
mp_obj_class_lookup(&lookup, &mp_type_object);
10401061
}
10411062

@@ -1073,27 +1094,28 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) {
10731094

10741095
const mp_obj_type_t *self = MP_OBJ_TO_PTR(object);
10751096

1076-
// for a const struct, this entry might be NULL
1077-
if (self->bases_tuple == NULL) {
1078-
return false;
1079-
}
1080-
1081-
// get the base objects (they should be type objects)
1082-
size_t len = self->bases_tuple->len;
1083-
mp_obj_t *items = self->bases_tuple->items;
1084-
if (len == 0) {
1097+
if (self->parent == NULL) {
1098+
// type has no parents
10851099
return false;
1086-
}
1087-
1088-
// iterate through the base objects
1089-
for (size_t i = 0; i < len - 1; i++) {
1090-
if (mp_obj_is_subclass_fast(items[i], classinfo)) {
1091-
return true;
1100+
} else if (((mp_obj_base_t*)self->parent)->type == &mp_type_tuple) {
1101+
// get the base objects (they should be type objects)
1102+
const mp_obj_tuple_t *parent_tuple = self->parent;
1103+
const mp_obj_t *item = parent_tuple->items;
1104+
const mp_obj_t *top = item + parent_tuple->len - 1;
1105+
1106+
// iterate through the base objects
1107+
for (; item < top; ++item) {
1108+
if (mp_obj_is_subclass_fast(*item, classinfo)) {
1109+
return true;
1110+
}
10921111
}
1093-
}
10941112

1095-
// search last base (simple tail recursion elimination)
1096-
object = items[len - 1];
1113+
// search last base (simple tail recursion elimination)
1114+
object = *item;
1115+
} else {
1116+
// type has 1 parent
1117+
object = MP_OBJ_FROM_PTR(self->parent);
1118+
}
10971119
}
10981120
}
10991121

0 commit comments

Comments
 (0)