py: Store bytecode arg names in bytecode (were in own array).

This saves a lot of RAM for 2 reasons:

1. For functions that don't have default values, var args or var kw
args (which is a large number of functions in the general case), the
mp_obj_fun_bc_t type now fits in 1 GC block (previously needed 2 because
of the extra pointer to point to the arg_names array).  So this saves 16
bytes per function (32 bytes on 64-bit machines).

2. Combining separate memory regions generally saves RAM because the
unused bytes at the end of the GC block are saved for 1 of the blocks
(since that block doesn't exist on its own anymore).  So generally this
saves 8 bytes per function.

Tested by importing lots of modules:

- 64-bit Linux gave about an 8% RAM saving for 86k of used RAM.
- pyboard gave about a 6% RAM saving for 31k of used RAM.
This commit is contained in:
Damien George
2014-10-25 15:07:02 +01:00
parent fcff4663dd
commit 1084b0f9c2
10 changed files with 58 additions and 33 deletions

20
py/bc.c
View File

@@ -93,7 +93,6 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
// usage for the common case of positional only args.
mp_obj_fun_bc_t *self = self_in;
mp_uint_t n_state = code_state->n_state;
const byte *ip = code_state->ip;
code_state->code_info = self->bytecode;
code_state->sp = &code_state->state[0] - 1;
@@ -153,13 +152,21 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
*var_pos_kw_args = dict;
}
// get pointer to arg_names array at start of bytecode prelude
const mp_obj_t *arg_names;
{
const byte *code_info = code_state->code_info;
mp_uint_t code_info_size = mp_decode_uint(&code_info);
arg_names = (const mp_obj_t*)(code_state->code_info + code_info_size);
}
for (mp_uint_t i = 0; i < n_kw; i++) {
qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]);
mp_obj_t wanted_arg_name = kwargs[2 * i];
for (mp_uint_t j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) {
if (arg_name == self->args[j]) {
if (wanted_arg_name == arg_names[j]) {
if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
"function got multiple values for argument '%s'", qstr_str(arg_name)));
"function got multiple values for argument '%s'", qstr_str(MP_OBJ_QSTR_VALUE(wanted_arg_name))));
}
code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
goto continue2;
@@ -202,13 +209,13 @@ continue2:;
if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) {
mp_map_elem_t *elem = NULL;
if (self->has_def_kw_args) {
elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, MP_OBJ_NEW_QSTR(self->args[self->n_pos_args + i]), MP_MAP_LOOKUP);
elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, arg_names[self->n_pos_args + i], MP_MAP_LOOKUP);
}
if (elem != NULL) {
code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value;
} else {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
"function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i])));
"function missing required keyword argument '%s'", qstr_str(MP_OBJ_QSTR_VALUE(arg_names[self->n_pos_args + i]))));
}
}
}
@@ -225,6 +232,7 @@ continue2:;
}
// bytecode prelude: initialise closed over variables
const byte *ip = code_state->ip;
for (mp_uint_t n_local = *ip++; n_local > 0; n_local--) {
mp_uint_t local_num = *ip++;
code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);