diff options
author | Michał Muskała <[email protected]> | 2018-05-09 19:41:24 +0200 |
---|---|---|
committer | Michał Muskała <[email protected]> | 2018-07-17 13:58:27 +0200 |
commit | f31db22102bde44c6ee17bc756bdeb109855acea (patch) | |
tree | 3342059d448e5d6af69b96b2bd2d0eaf84ffc755 /erts/emulator/beam | |
parent | 9ae2044073e6433030ce30756658b103ce67c3c1 (diff) | |
download | otp-f31db22102bde44c6ee17bc756bdeb109855acea.tar.gz otp-f31db22102bde44c6ee17bc756bdeb109855acea.tar.bz2 otp-f31db22102bde44c6ee17bc756bdeb109855acea.zip |
Optimise creation of anonymous functions
This introduces a similar optimisation for normal funs
to what was introduced for external funs in #1725.
It is possible to allocate the fun as a literal, if it does not capture
the environment (i.e. it does not close over any variables).
Unfortunately it's not possible to do this in the compiler due to
problems with representation of such functions in the `.beam` files.
Fortunately, we can do this in the loader.
Simple evaluation shows that functions that don't capture the
enviornment consistute over 60% of all funs in the source code of
Erlang/OTP itself.
The only downside is that we lose a meningful value in the `pid` field
of the fun. The goal of this field, beyond debugging, was to be
able to identify the original node of a function. To be able to still do
this, the functions that are created in the loader are assigned the init
pid as the creator.
To solve issues with staryp, initially set the `erts_init_process_id`
to `ERTS_INVALID_PID` and skip the described optimisation if the value
is still uninitialised.
Diffstat (limited to 'erts/emulator/beam')
-rw-r--r-- | erts/emulator/beam/beam_load.c | 52 | ||||
-rw-r--r-- | erts/emulator/beam/break.c | 1 | ||||
-rw-r--r-- | erts/emulator/beam/erl_init.c | 3 | ||||
-rw-r--r-- | erts/emulator/beam/erl_process_dump.c | 6 |
4 files changed, 51 insertions, 11 deletions
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index e61199a8fd..50cbb37f3e 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4245,21 +4245,55 @@ gen_make_fun2(LoaderState* stp, GenOpArg idx) { ErlFunEntry* fe; GenOp* op; + Uint arity, num_free; if (idx.val >= stp->num_lambdas) { - stp->lambda_error = "missing or short chunk 'FunT'"; - fe = 0; + stp->lambda_error = "missing or short chunk 'FunT'"; + fe = 0; + num_free = 0; + arity = 0; } else { - fe = stp->lambdas[idx.val].fe; + fe = stp->lambdas[idx.val].fe; + num_free = stp->lambdas[idx.val].num_free; + arity = fe->arity; } NEW_GENOP(stp, op); - op->op = genop_i_make_fun_2; - op->arity = 2; - op->a[0].type = TAG_u; - op->a[0].val = (BeamInstr) fe; - op->a[1].type = TAG_u; - op->a[1].val = stp->lambdas[idx.val].num_free; + + /* + * It's possible this is called before init process is started, + * skip the optimisation in such case. + */ + if (num_free == 0 && erts_init_process_id != ERTS_INVALID_PID) { + Uint lit; + Eterm* hp; + ErlFunThing* funp; + + lit = new_literal(stp, &hp, ERL_FUN_SIZE); + funp = (ErlFunThing *) hp; + erts_refc_inc(&fe->refc, 2); + funp->thing_word = HEADER_FUN; + funp->next = NULL; + funp->fe = fe; + funp->num_free = 0; + funp->creator = erts_init_process_id; + funp->arity = arity; + + op->op = genop_move_2; + op->arity = 2; + op->a[0].type = TAG_q; + op->a[0].val = lit; + op->a[1].type = TAG_x; + op->a[1].val = 0; + } else { + op->op = genop_i_make_fun_2; + op->arity = 2; + op->a[0].type = TAG_u; + op->a[0].val = (BeamInstr) fe; + op->a[1].type = TAG_u; + op->a[1].val = num_free; + } + op->next = NULL; return op; } diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 9ff52c92b8..81531f6cc8 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -108,6 +108,7 @@ process_killer(void) erts_exit(0, ""); switch(j) { case 'k': + ASSERT(erts_init_process_id != ERTS_INVALID_PID); /* Send a 'kill' exit signal from init process */ erts_proc_sig_send_exit(NULL, erts_init_process_id, rp->common.id, am_kill, NIL, diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 57c6c10c7f..5da7b43b9e 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -128,7 +128,7 @@ const Eterm etp_hole_marker = 0; static int modified_sched_thread_suggested_stack_size = 0; -Eterm erts_init_process_id; +Eterm erts_init_process_id = ERTS_INVALID_PID; /* * Note about VxWorks: All variables must be initialized by executable code, @@ -2258,6 +2258,7 @@ erl_start(int argc, char **argv) erts_init_process_id = erl_first_process_otp("otp_ring0", NULL, 0, boot_argc, boot_argv); + ASSERT(erts_init_process_id != ERTS_INVALID_PID); { /* diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 243db4c734..706530023b 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -963,12 +963,16 @@ dump_module_literals(fmtfn_t to, void *to_arg, ErtsLiteralArea* lit_area) } erts_putc(to, to_arg, '\n'); } - } else if (is_export_header(w)) { + } else if (is_export_header(w) || is_fun_header(w)) { dump_externally(to, to_arg, term); erts_putc(to, to_arg, '\n'); } size = 1 + header_arity(w); switch (w & _HEADER_SUBTAG_MASK) { + case FUN_SUBTAG: + ASSERT(((ErlFunThing*)(htop))->num_free == 0); + size += 1; + break; case MAP_SUBTAG: if (is_flatmap_header(w)) { size += 1 + flatmap_get_size(htop); |