From 7b89ad8dbf432ab51eea6d138e179bf51394c786 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 19 Aug 2021 22:46:40 +1000 Subject: [PATCH] py/vm: Add a fast path for LOAD_ATTR on instance types. When the LOAD_ATTR opcode is executed there are quite a few different cases that have to be handled, but the common case is accessing a member on an instance type. Typically, built-in types provide methods which is why this is common. Fortunately, for this specific case, if the member is found in the member map then there's no further processing. This optimisation does a relatively cheap check (type is instance) and then forwards directly to the member map lookup, falling back to the regular path if necessary. Signed-off-by: Jim Mussared --- py/mpconfig.h | 6 ++++++ py/objtype.c | 1 + py/runtime.c | 4 ++++ py/vm.c | 21 ++++++++++++++++++++- 4 files changed, 31 insertions(+), 1 deletion(-) 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