Re: hipe_mfait_lock

Louis-Philippe Gauthier <[email protected]>
Newsgroups gmane.comp.lang.erlang.bugs
Message-ID <CAGvQrRjZ22=f3XM19X-PV8zAN0stEh=WY7s9jYxr6auAMbvMmw@mail.gmail.com>
Hi Mikael,
Awesome work, I'll give the patch a try this week.

Thanks,
LP

On Mon, Feb 23, 2015 at 6:30 AM, Mikael Pettersson <[email protected]>
wrote:

> Louis-Philippe Gauthier writes:
>  > Hi Sverker,
>  > That's not a bad idea, I'll try that approach!
>  >
>  > Thanks,
>  > LP
>  >
>  > On Thu, Jan 15, 2015 at 12:27 AM, Sverker Eriksson <
>  > [email protected]> wrote:
>  >
>  > >  One quite simple improvement is to change the hipe_mfait_lock to be a
>  > > read-write mutex (erts_smp_rwmtx_t).
>  > > And then do read-lock at lookup and exclusive write-lock only when
>  > > inserting a new entry in the table.
>  > >
>  > > I don't think that requires much knowledge about the VM to pull off
> ;-) .
>  > >
>  > > /Sverker, Erlang/OTP
>  > >
>  > >
>  > >
>  > >
>  > > On 01/14/2015 12:34 AM, Louis-Philippe Gauthier wrote:
>  > >
>  > > Hi erlang-bugs,
>  > > I couple of months ago I tried running our full application using
> HiPE. I
>  > > ran into several issues, some manageable, some not... The most
> problematic
>  > > issue was due to locking, specifically, the hipe_mfait_lock (you can
> see
>  > > the lock counter output in the gist bellow).
>  > > https://gist.github.com/lpgauth/2b3220f4bceeed6f62d0
>  > >
>  > > Looking at the code it's obvious that this is a known problem... the
>  > > following comment was added when the lock was added in 2010.
>  > >
>  > > "XXX: Redesign apply et al to avoid those updates."
> https://github.com/erlang/otp/blob/maint/erts/emulator/hipe/hipe_bif0.c#L1218
>  > >
>  > > Unfortunately, I'm don't know the runtime enough to start patching
> it... so
>  > > instead I'm reporting it.
>
> Please try the attached patch (against 17.4, should apply to older
> versions too by I haven't checked), and let me know if it makes a
> noticeable improvement to your use case.
>
> Run-time apply:s now do their lookups using a shared read-lock, and
> only if their lookups fail do they re-lock with an exclusive write-lock
> in order to insert new data.  Module loading functions always take
> exclusive locks, which matches the current behaviour.
>
> /Mikael
>
>
> --- otp_src_17.4/erts/emulator/hipe/hipe_bif0.c.~1~     2014-12-09
> 21:11:07.000000000 +0100
> +++ otp_src_17.4/erts/emulator/hipe/hipe_bif0.c 2015-02-22
> 21:32:55.361884741 +0100
> @@ -1217,22 +1217,32 @@ static struct {
>       * they create a new stub for the mfa, which forces locking.
>       * XXX: Redesign apply et al to avoid those updates.
>       */
> -    erts_smp_mtx_t lock;
> +    erts_smp_rwmtx_t lock;
>  } hipe_mfa_info_table;
>
>  static inline void hipe_mfa_info_table_init_lock(void)
>  {
> -    erts_smp_mtx_init(&hipe_mfa_info_table.lock, "hipe_mfait_lock");
> +    erts_smp_rwmtx_init(&hipe_mfa_info_table.lock, "hipe_mfait_lock");
>  }
>
> -static inline void hipe_mfa_info_table_lock(void)
> +static inline void hipe_mfa_info_table_rlock(void)
>  {
> -    erts_smp_mtx_lock(&hipe_mfa_info_table.lock);
> +    erts_smp_rwmtx_rlock(&hipe_mfa_info_table.lock);
>  }
>
> -static inline void hipe_mfa_info_table_unlock(void)
> +static inline void hipe_mfa_info_table_runlock(void)
>  {
> -    erts_smp_mtx_unlock(&hipe_mfa_info_table.lock);
> +    erts_smp_rwmtx_runlock(&hipe_mfa_info_table.lock);
> +}
> +
> +static inline void hipe_mfa_info_table_rwlock(void)
> +{
> +    erts_smp_rwmtx_rwlock(&hipe_mfa_info_table.lock);
> +}
> +
> +static inline void hipe_mfa_info_table_rwunlock(void)
> +{
> +    erts_smp_rwmtx_rwunlock(&hipe_mfa_info_table.lock);
>  }
>
>  #define HIPE_MFA_HASH(M,F,A)   ((M) * (F) + (A))
> @@ -1333,6 +1343,7 @@ void *hipe_mfa_find_na(Eterm m, Eterm f,
>  }
>  #endif
>
> +/* PRE: called with write lock held */
>  static struct hipe_mfa_info *hipe_mfa_info_table_put_locked(Eterm m,
> Eterm f, unsigned int arity)
>  {
>      unsigned long h;
> @@ -1362,7 +1373,7 @@ static void hipe_mfa_set_na(Eterm m, Ete
>  {
>      struct hipe_mfa_info *p;
>
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      p = hipe_mfa_info_table_put_locked(m, f, arity);
>  #ifdef DEBUG_LINKER
>      printf("%s: ", __FUNCTION__);
> @@ -1372,7 +1383,7 @@ static void hipe_mfa_set_na(Eterm m, Ete
>      p->local_address = address;
>      if (is_exported)
>         p->remote_address = address;
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>  }
>
>  #if defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__) ||
> defined(__arm__)
> @@ -1381,10 +1392,10 @@ void *hipe_mfa_get_trampoline(Eterm m, E
>      struct hipe_mfa_info *p;
>      void *trampoline;
>
> -    hipe_mfa_info_table_lock();
> -    p = hipe_mfa_info_table_put_locked(m, f, arity);
> -    trampoline = p->trampoline;
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rlock();
> +    p = hipe_mfa_info_table_get_locked(m, f, arity);
> +    trampoline = p ? p->trampoline : NULL;
> +    hipe_mfa_info_table_runlock();
>      return trampoline;
>  }
>
> @@ -1392,10 +1403,10 @@ void hipe_mfa_set_trampoline(Eterm m, Et
>  {
>      struct hipe_mfa_info *p;
>
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      p = hipe_mfa_info_table_put_locked(m, f, arity);
>      p->trampoline = trampoline;
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>  }
>  #endif
>
> @@ -1426,7 +1437,7 @@ BIF_RETTYPE hipe_bifs_invalidate_funinfo
>      struct mfa mfa;
>      struct hipe_mfa_info *p;
>
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      lst = BIF_ARG_1;
>      while (is_list(lst)) {
>         if (!term_to_mfa(CAR(list_val(lst)), &mfa))
> @@ -1455,7 +1466,7 @@ BIF_RETTYPE hipe_bifs_invalidate_funinfo
>             }
>         }
>      }
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>      if (is_not_nil(lst))
>         BIF_ERROR(BIF_P, BADARG);
>      BIF_RET(NIL);
> @@ -1469,7 +1480,7 @@ void hipe_mfa_save_orig_beam_op(Eterm mo
>      orig_beam_op = pc[0];
>      if (orig_beam_op != BeamOpCode(op_hipe_trap_call_closure) &&
>         orig_beam_op != BeamOpCode(op_hipe_trap_call)) {
> -       hipe_mfa_info_table_lock();
> +       hipe_mfa_info_table_rwlock();
>         p = hipe_mfa_info_table_put_locked(mod, fun, ari);
>  #ifdef DEBUG_LINKER
>         printf("%s: ", __FUNCTION__);
> @@ -1478,7 +1489,7 @@ void hipe_mfa_save_orig_beam_op(Eterm mo
>  #endif
>         p->beam_code = pc;
>         p->orig_beam_op = orig_beam_op;
> -       hipe_mfa_info_table_unlock();
> +       hipe_mfa_info_table_rwunlock();
>      } else {
>  #ifdef DEBUG_LINKER
>         printf("%s: ", __FUNCTION__);
> @@ -1505,7 +1516,8 @@ static void *hipe_make_stub(Eterm m, Ete
>      return StubAddress;
>  }
>
> -static void *hipe_get_na_nofail_locked(Eterm m, Eterm f, unsigned int a,
> int is_remote)
> +/* PRE: called with read or write lock held */
> +static void *hipe_get_na_try_locked(Eterm m, Eterm f, unsigned int a, int
> is_remote, struct hipe_mfa_info **pp)
>  {
>      struct hipe_mfa_info *p;
>      void *address;
> @@ -1523,7 +1535,20 @@ static void *hipe_get_na_nofail_locked(E
>         address = p->remote_address;
>         if (address)
>             return address;
> -    } else
> +    }
> +    /* Caller must take the slow path with the write lock held, but allow
> +       it to avoid some work if it already holds the write lock.  */
> +    if (pp)
> +       *pp = p;
> +    return NULL;
> +}
> +
> +/* PRE: called with write lock held */
> +static void *hipe_get_na_slow_locked(Eterm m, Eterm f, unsigned int a,
> int is_remote, struct hipe_mfa_info *p)
> +{
> +    void *address;
> +
> +    if (!p)
>         p = hipe_mfa_info_table_put_locked(m, f, a);
>      address = hipe_make_stub(m, f, a, is_remote);
>      /* XXX: how to tell if a BEAM MFA is exported or not? */
> @@ -1531,14 +1556,34 @@ static void *hipe_get_na_nofail_locked(E
>      return address;
>  }
>
> +/* PRE: called with write lock held */
> +static void *hipe_get_na_nofail_locked(Eterm m, Eterm f, unsigned int a,
> int is_remote)
> +{
> +    struct hipe_mfa_info *p /*= NULL*/;
> +    void *address;
> +
> +    address = hipe_get_na_try_locked(m, f, a, is_remote, &p);
> +    if (address)
> +       return address;
> +
> +    address = hipe_get_na_slow_locked(m, f, a, is_remote, p);
> +    return address;
> +}
> +
>  static void *hipe_get_na_nofail(Eterm m, Eterm f, unsigned int a, int
> is_remote)
>  {
> -    void *p;
> +    void *address;
>
> -    hipe_mfa_info_table_lock();
> -    p = hipe_get_na_nofail_locked(m, f, a, is_remote);
> -    hipe_mfa_info_table_unlock();
> -    return p;
> +    hipe_mfa_info_table_rlock();
> +    address = hipe_get_na_try_locked(m, f, a, is_remote, NULL);
> +    hipe_mfa_info_table_runlock();
> +    if (address)
> +       return address;
> +
> +    hipe_mfa_info_table_rwlock();
> +    address = hipe_get_na_slow_locked(m, f, a, is_remote, NULL);
> +    hipe_mfa_info_table_rwunlock();
> +    return address;
>  }
>
>  /* used for apply/3 in hipe_mode_switch */
> @@ -1617,7 +1662,7 @@ int hipe_find_mfa_from_ra(const void *ra
>      /* Note about locking: the table is only updated from the
>         loader, which runs with the rest of the system suspended. */
>      /* XXX: alas not true; see comment at hipe_mfa_info_table.lock */
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      bucket = hipe_mfa_info_table.bucket;
>      nrbuckets = 1 << hipe_mfa_info_table.log2size;
>      mfa = NULL;
> @@ -1638,7 +1683,7 @@ int hipe_find_mfa_from_ra(const void *ra
>         *f = mfa->f;
>         *a = mfa->a;
>      }
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>      return mfa ? 1 : 0;
>  }
>
> @@ -1715,7 +1760,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIS
>        default:
>         goto badarg;
>      }
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      callee_mfa = hipe_mfa_info_table_put_locked(callee.mod, callee.fun,
> callee.ari);
>      caller_mfa = hipe_mfa_info_table_put_locked(caller.mod, caller.fun,
> caller.ari);
>
> @@ -1731,7 +1776,7 @@ BIF_RETTYPE hipe_bifs_add_ref_2(BIF_ALIS
>      ref->flags = flags;
>      ref->next = callee_mfa->referred_from;
>      callee_mfa->referred_from = ref;
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>
>      BIF_RET(NIL);
>
> @@ -1751,12 +1796,12 @@ BIF_RETTYPE hipe_bifs_mark_referred_from
>
>      if (!term_to_mfa(BIF_ARG_1, &mfa))
>         BIF_ERROR(BIF_P, BADARG);
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      p = hipe_mfa_info_table_get_locked(mfa.mod, mfa.fun, mfa.ari);
>      if (p)
>         for (ref = p->referred_from; ref != NULL; ref = ref->next)
>             ref->flags |= REF_FLAG_PENDING_REDIRECT;
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>      BIF_RET(NIL);
>  }
>
> @@ -1770,7 +1815,7 @@ static void hipe_purge_all_refs(void)
>      struct hipe_mfa_info **bucket;
>      unsigned int i, nrbuckets;
>
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>
>      bucket = hipe_mfa_info_table.bucket;
>      nrbuckets = 1 << hipe_mfa_info_table.log2size;
> @@ -1792,7 +1837,7 @@ static void hipe_purge_all_refs(void)
>             erts_free(ERTS_ALC_T_HIPE, mfa);
>         }
>      }
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>  }
>
>  BIF_RETTYPE hipe_bifs_remove_refs_from_1(BIF_ALIST_1)
> @@ -1809,7 +1854,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1
>
>      if (!term_to_mfa(BIF_ARG_1, &mfa))
>         BIF_ERROR(BIF_P, BADARG);
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      caller_mfa = hipe_mfa_info_table_get_locked(mfa.mod, mfa.fun,
> mfa.ari);
>      if (caller_mfa) {
>         refers_to = caller_mfa->refers_to;
> @@ -1840,7 +1885,7 @@ BIF_RETTYPE hipe_bifs_remove_refs_from_1
>         }
>         caller_mfa->refers_to = NULL;
>      }
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>      BIF_RET(am_ok);
>  }
>
> @@ -1859,7 +1904,7 @@ BIF_RETTYPE hipe_bifs_redirect_referred_
>
>      if (!term_to_mfa(BIF_ARG_1, &mfa))
>         BIF_ERROR(BIF_P, BADARG);
> -    hipe_mfa_info_table_lock();
> +    hipe_mfa_info_table_rwlock();
>      p = hipe_mfa_info_table_get_locked(mfa.mod, mfa.fun, mfa.ari);
>      if (p) {
>         prev = &p->referred_from;
> @@ -1890,7 +1935,7 @@ BIF_RETTYPE hipe_bifs_redirect_referred_
>             }
>         }
>      }
> -    hipe_mfa_info_table_unlock();
> +    hipe_mfa_info_table_rwunlock();
>      BIF_RET(NIL);
>  }
>
>
>

_______________________________________________
erlang-bugs mailing list
[email protected]
http://erlang.org/mailman/listinfo/erlang-bugs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.