summaryrefslogtreecommitdiff
path: root/py/objfun.c
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2019-09-16 22:12:59 +1000
committerDamien George <damien.p.george@gmail.com>2019-10-01 12:26:22 +1000
commitb5ebfadbd615de42c43851f27a062bacd9147996 (patch)
treee4602e96a0eaf9ee0c30913dbabfe9013dda617a /py/objfun.c
parent81d04a0200e0d4038c011e4946bfae5707ef9d9c (diff)
py: Compress first part of bytecode prelude.
The start of the bytecode prelude contains 6 numbers telling the amount of stack needed for the Python values and exceptions, and the signature of the function. Prior to this patch these numbers were all encoded one after the other (2x variable unsigned integers, then 4x bytes), but using so many bytes is unnecessary. An entropy analysis of around 150,000 bytecode functions from the CPython standard library showed that the optimal Shannon coding would need about 7.1 bits on average to encode these 6 numbers, compared to the existing 48 bits. This patch attempts to get close to this optimal value by packing the 6 numbers into a single, varible-length unsigned integer via bit-wise interleaving. The interleaving scheme is chosen to minimise the average number of bytes needed, and at the same time keep the scheme simple enough so it can be implemented without too much overhead in code size or speed. The scheme requires about 10.5 bits on average to store the 6 numbers. As a result most functions which originally took 6 bytes to encode these 6 numbers now need only 1 byte (in 80% of cases).
Diffstat (limited to 'py/objfun.c')
-rw-r--r--py/objfun.c23
1 files changed, 10 insertions, 13 deletions
diff --git a/py/objfun.c b/py/objfun.c
index 114367b4e..053fe46b7 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -161,12 +161,7 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) {
#endif
const byte *bc = fun->bytecode;
- bc = mp_decode_uint_skip(bc); // skip n_state
- bc = mp_decode_uint_skip(bc); // skip n_exc_stack
- bc++; // skip scope_params
- bc++; // skip n_pos_args
- bc++; // skip n_kwonly_args
- bc++; // skip n_def_pos_args
+ MP_BC_PRELUDE_SIG_DECODE(bc);
return mp_obj_code_get_name(bc);
}
@@ -197,10 +192,10 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) {
#define DECODE_CODESTATE_SIZE(bytecode, n_state_out_var, state_size_out_var) \
{ \
- /* bytecode prelude: state size and exception stack size */ \
- n_state_out_var = mp_decode_uint_value(bytecode); \
- size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(bytecode)); \
- \
+ const uint8_t *ip = bytecode; \
+ size_t n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args; \
+ MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args); \
+ \
/* state size in bytes */ \
state_size_out_var = n_state_out_var * sizeof(mp_obj_t) \
+ n_exc_stack * sizeof(mp_exc_stack_t); \
@@ -295,9 +290,11 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
assert(0);
}
}
- const byte *bytecode_ptr = mp_decode_uint_skip(mp_decode_uint_skip(self->bytecode));
- size_t n_pos_args = bytecode_ptr[1];
- size_t n_kwonly_args = bytecode_ptr[2];
+ const byte *bytecode_ptr = self->bytecode;
+ size_t n_state_unused, n_exc_stack_unused, scope_flags_unused;
+ size_t n_pos_args, n_kwonly_args, n_def_args_unused;
+ MP_BC_PRELUDE_SIG_DECODE_INTO(bytecode_ptr, n_state_unused, n_exc_stack_unused,
+ scope_flags_unused, n_pos_args, n_kwonly_args, n_def_args_unused);
// We can't check the case when an exception is returned in state[0]
// and there are no arguments, because in this case our detection slot may have
// been overwritten by the returned exception (which is allowed).