This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
optimise save/restore of PL_delaymagic.
authorDavid Mitchell <davem@iabyn.com>
Tue, 13 Oct 2015 16:02:39 +0000 (17:02 +0100)
committerDavid Mitchell <davem@iabyn.com>
Sun, 18 Oct 2015 11:04:27 +0000 (12:04 +0100)
A few places (pp_push, pp_unshift, pp_aassign) have to
set PL_delaymagic on entry, and restore it on exit. These are hot
pieces of code. Rather than using  ENTER/SAVEI16(PL_delaymagic)/LEAVE,
add an extra field to the jumpenv struct, and make the JUMPENV_PUSH / POP
macros automatically save and restore this var.

This means that pp_push etc only need to do a local save:

    U16 old_delaymagic = PL_delaymagic;
    PL_delaymagic = DM_DELAY;
    ....
    PL_delaymagic = old_delaymagic;

and in case of an exception being raised, PL_delaymagic still gets
restored.

This transfers the cost of saving PL_delaymagic from each call to
pp_aassign etc to each time a new run level is invoked. The latter should
be much less frequent.

Note that prior to this commit, pp_aassign wasn't actually saving and
restoring PL_delaymagic; it was just setting it to 0 at the end. So this
commit also makes pp_aassign safe against PL_delaymagic re-entrancy like
pp_push and pp_unshift already were.

cop.h
intrpvar.h
pp.c
pp_hot.c

diff --git a/cop.h b/cop.h
index aae9cea..d36d189 100644 (file)
--- a/cop.h
+++ b/cop.h
@@ -34,6 +34,7 @@ struct jmpenv {
     Sigjmp_buf         je_buf;         /* uninit if je_prev is NULL */
     int                        je_ret;         /* last exception thrown */
     bool               je_mustcatch;   /* need to call longjmp()? */
+    U16                 je_old_delaymagic; /* saved PL_delaymagic */
 };
 
 typedef struct jmpenv JMPENV;
@@ -55,6 +56,7 @@ typedef struct jmpenv JMPENV;
        PL_start_env.je_prev = NULL;            \
        PL_start_env.je_ret = -1;               \
        PL_start_env.je_mustcatch = TRUE;       \
+       PL_start_env.je_old_delaymagic = 0;     \
     } STMT_END
 
 /*
@@ -103,6 +105,7 @@ typedef struct jmpenv JMPENV;
        cur_env.je_ret = PerlProc_setjmp(cur_env.je_buf, SCOPE_SAVES_SIGNAL_MASK);              \
        PL_top_env = &cur_env;                                          \
        cur_env.je_mustcatch = FALSE;                                   \
+       cur_env.je_old_delaymagic = PL_delaymagic;                      \
        (v) = cur_env.je_ret;                                           \
     } STMT_END
 
@@ -114,6 +117,7 @@ typedef struct jmpenv JMPENV;
            Perl_deb(aTHX_ "JUMPENV_POP level=%d at %s:%d\n",           \
                         i, __FILE__, __LINE__);})                      \
        assert(PL_top_env == &cur_env);                                 \
+       PL_delaymagic = cur_env.je_old_delaymagic;                      \
        PL_top_env = cur_env.je_prev;                                   \
     } STMT_END
 
index 79bddeb..7dc4be4 100644 (file)
@@ -76,7 +76,22 @@ PERLVAR(I, curpm,    PMOP *)         /* what to do \ interps in REs from */
 
 PERLVAR(I, tainting,   bool)           /* doing taint checks */
 PERLVAR(I, tainted,    bool)           /* using variables controlled by $< */
+
+/* PL_delaymagic is currently used for two purposes: to assure simultaneous
+ * updates in ($<,$>) = ..., and to assure atomic update in push/unshift
+ * @ISA, It works like this: a few places such as pp_push set the DM_DELAY
+ * flag; then various places such as av_store() skip mg_set(ary) if this
+ * flag is set, and various magic vtable methods set flags like
+ * DM_ARRAY_ISA if they've seen something of that ilk. Finally when
+ * control returns to pp_push or whatever, it sees if any of those flags
+ * have been set, and if so finally calls mg_set().
+ *
+ * NB: PL_delaymagic is automatically saved and restored by JUMPENV_PUSH
+ * / POP. This removes the need to do ENTER/SAVEI16(PL_delaymagic)/LEAVE
+ * in hot code like pp_push.
+ */
 PERLVAR(I, delaymagic, U16)            /* ($<,$>) = ... */
+
 PERLVAR(I, localizing, U8)             /* are we processing a local() list? */
 PERLVAR(I, in_eval,    U8)             /* trap "fatal" errors? */
 PERLVAR(I, defgv,      GV *)           /* the *_ glob */
diff --git a/pp.c b/pp.c
index b84747a..6e9995a 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -5443,9 +5443,11 @@ PP(pp_push)
        /* SPAGAIN; not needed: SP is assigned to immediately below */
     }
     else {
+        /* PL_delaymagic is restored by JUMPENV_POP on dieing, so we
+         * only need to save locally, not on the save stack */
+        U16 old_delaymagic = PL_delaymagic;
+
        if (SvREADONLY(ary) && MARK < SP) Perl_croak_no_modify();
-        ENTER;
-        SAVEI16(PL_delaymagic);
        PL_delaymagic = DM_DELAY;
        for (++MARK; MARK <= SP; MARK++) {
            SV *sv;
@@ -5457,7 +5459,7 @@ PP(pp_push)
        }
        if (PL_delaymagic & DM_ARRAY_ISA)
            mg_set(MUTABLE_SV(ary));
-        LEAVE;
+        PL_delaymagic = old_delaymagic;
     }
     SP = ORIGMARK;
     if (OP_GIMME(PL_op, 0) != G_VOID) {
@@ -5497,10 +5499,12 @@ PP(pp_unshift)
        /* SPAGAIN; not needed: SP is assigned to immediately below */
     }
     else {
+        /* PL_delaymagic is restored by JUMPENV_POP on dieing, so we
+         * only need to save locally, not on the save stack */
+        U16 old_delaymagic = PL_delaymagic;
        SSize_t i = 0;
+
        av_unshift(ary, SP - MARK);
-        ENTER;
-        SAVEI16(PL_delaymagic);
         PL_delaymagic = DM_DELAY;
        while (MARK < SP) {
            SV * const sv = newSVsv(*++MARK);
@@ -5508,7 +5512,7 @@ PP(pp_unshift)
        }
         if (PL_delaymagic & DM_ARRAY_ISA)
             mg_set(MUTABLE_SV(ary));
-        LEAVE;
+        PL_delaymagic = old_delaymagic;
     }
     SP = ORIGMARK;
     if (OP_GIMME(PL_op, 0) != G_VOID) {
index 9ac6066..e866841 100644 (file)
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -1174,6 +1174,9 @@ PP(pp_aassign)
     SSize_t i;
     int magic;
     U32 lval;
+    /* PL_delaymagic is restored by JUMPENV_POP on dieing, so we
+     * only need to save locally, not on the save stack */
+    U16 old_delaymagic = PL_delaymagic;
 #ifdef DEBUGGING
     bool fake = 0;
 #endif
@@ -1545,7 +1548,7 @@ PP(pp_aassign)
         PERL_UNUSED_VAR(tmp_egid);
 #endif
     }
-    PL_delaymagic = 0;
+    PL_delaymagic = old_delaymagic;
 
     if (gimme == G_VOID)
        SP = firstrelem - 1;