diff --git a/py/mpconfig.h b/py/mpconfig.h index 7b98112e17..2fbf1490d8 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -527,6 +527,12 @@ #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (0) #endif +// Optimise the fast path for loading attributes from instance types. Increases +// Thumb2 code size by about 48 bytes. +#ifndef MICROPY_OPT_LOAD_ATTR_FAST_PATH +#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (0) +#endif + // Whether to use fast versions of bitwise operations (and, or, xor) when the // arguments are both positive. Increases Thumb2 code size by about 250 bytes. #ifndef MICROPY_OPT_MPZ_BITWISE diff --git a/py/objtype.c b/py/objtype.c index 508bab99d3..0977a67ced 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -579,6 +579,7 @@ STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des assert(mp_obj_is_instance_type(mp_obj_get_type(self_in))); mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); + // Note: This is fast-path'ed in the VM for the MP_BC_LOAD_ATTR operation. mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); if (elem != NULL) { // object member, always treated as a value diff --git a/py/runtime.c b/py/runtime.c index 27e5bc13eb..2f7cf1fa60 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1081,6 +1081,10 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_NULL; dest[1] = MP_OBJ_NULL; + // Note: the specific case of obj being an instance type is fast-path'ed in the VM + // for the MP_BC_LOAD_ATTR opcode. Instance types handle type->attr and look up directly + // in their member's map. + // get the type const mp_obj_type_t *type = mp_obj_get_type(obj); diff --git a/py/vm.c b/py/vm.c index bbfc9914eb..e5a62e4415 100644 --- a/py/vm.c +++ b/py/vm.c @@ -414,7 +414,26 @@ dispatch_loop: FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; - SET_TOP(mp_load_attr(TOP(), qst)); + mp_obj_t top = TOP(); + mp_obj_t obj; + #if MICROPY_OPT_LOAD_ATTR_FAST_PATH + // For the specific case of an instance type, it implements .attr + // and forwards to its members map. Attribute lookups on instance + // types are extremely common, so avoid all the other checks and + // calls that normally happen first. + mp_map_elem_t *elem = NULL; + if (mp_obj_is_instance_type(mp_obj_get_type(top))) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); + elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + } + if (elem) { + obj = elem->value; + } else + #endif + { + obj = mp_load_attr(top, qst); + } + SET_TOP(obj); DISPATCH(); } #else