What is the fastest way to check if a function is defined in a module?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

What is the fastest way to check if a function is defined in a module?

Metin Akat
I'm looking into this code: https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L631

Seems like this try/catch clause is implemented with the idea that most of the time it will succeed, as the callback module will have implemented the corresponding callback function, so we will not have to go into the exception clause where it needs to call erlang:function_exported/2 one more time (presumably as an additional safeguard) which is more expensive than an optimistic try clause.

I am writing something very similar, where such a check (is a function exported) will be performed by thousands/millions of processes on every message, but in my case... it's quite probable that most of the time this WILL raise the exception. In my case the function will be purely optional - if defined, it will return a value. If not, a default value will be used. 

So my question is, how bad is this? I will test, but I'm still interested if someone has any ideas (maybe do something completely different).

Other possibilities are:

- Actually make the callback compulsory. I don't want to do that, as the problem I'm solving with this project is "See, you don't have to implement this boilerplate every time any more"

-  Put the option in application env where the user can configure it on startup. 



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Zachary Kessin-2
You can use the function MODULE:module_info/1 something like 
is_defined(MyFunc, Arity, MODULE)->
   Functions = MODULE:module_info(functions),
   lists:member({MyFunc,Arity}, Functions).

Where MyFunc is an atom of course

You could also expand that to a call_function_if_defined_with_default/N but i will leave that to you

Zach


Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
+972 54 234 3956 / +44 203 734 9790 / +1 617 778 7213

On Sat, Mar 3, 2018 at 9:42 PM, Metin Akat <[hidden email]> wrote:
I'm looking into this code: https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L631

Seems like this try/catch clause is implemented with the idea that most of the time it will succeed, as the callback module will have implemented the corresponding callback function, so we will not have to go into the exception clause where it needs to call erlang:function_exported/2 one more time (presumably as an additional safeguard) which is more expensive than an optimistic try clause.

I am writing something very similar, where such a check (is a function exported) will be performed by thousands/millions of processes on every message, but in my case... it's quite probable that most of the time this WILL raise the exception. In my case the function will be purely optional - if defined, it will return a value. If not, a default value will be used. 

So my question is, how bad is this? I will test, but I'm still interested if someone has any ideas (maybe do something completely different).

Other possibilities are:

- Actually make the callback compulsory. I don't want to do that, as the problem I'm solving with this project is "See, you don't have to implement this boilerplate every time any more"

-  Put the option in application env where the user can configure it on startup. 



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Zachary Kessin-2
I realize that there is a race condition in that code, if someone reloads the module at the wrong moment it could crash.



Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
+972 54 234 3956 / +44 203 734 9790 / +1 617 778 7213

On Sun, Mar 4, 2018 at 8:37 AM, Zachary Kessin <[hidden email]> wrote:
You can use the function MODULE:module_info/1 something like 
is_defined(MyFunc, Arity, MODULE)->
   Functions = MODULE:module_info(functions),
   lists:member({MyFunc,Arity}, Functions).

Where MyFunc is an atom of course

You could also expand that to a call_function_if_defined_with_default/N but i will leave that to you

Zach


Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
<a href="tel:054-234-3956" value="+972542343956" target="_blank">+972 54 234 3956 / <a href="tel:+44%2020%203734%209790" value="+442037349790" target="_blank">+44 203 734 9790 / <a href="tel:+1%20617-778-7213" value="+16177787213" target="_blank">+1 617 778 7213

On Sat, Mar 3, 2018 at 9:42 PM, Metin Akat <[hidden email]> wrote:
I'm looking into this code: https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L631

Seems like this try/catch clause is implemented with the idea that most of the time it will succeed, as the callback module will have implemented the corresponding callback function, so we will not have to go into the exception clause where it needs to call erlang:function_exported/2 one more time (presumably as an additional safeguard) which is more expensive than an optimistic try clause.

I am writing something very similar, where such a check (is a function exported) will be performed by thousands/millions of processes on every message, but in my case... it's quite probable that most of the time this WILL raise the exception. In my case the function will be purely optional - if defined, it will return a value. If not, a default value will be used. 

So my question is, how bad is this? I will test, but I'm still interested if someone has any ideas (maybe do something completely different).

Other possibilities are:

- Actually make the callback compulsory. I don't want to do that, as the problem I'm solving with this project is "See, you don't have to implement this boilerplate every time any more"

-  Put the option in application env where the user can configure it on startup. 



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions




_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Metin Akat
I haven't yet done rigorous testing with many processes, but what you are suggesting is the slowest so far (at least by the most naive way of measuring, while not under load) :

37> timer:tc(fun() -> lists:member({not_a_function, 1}, Module:module_info(exports)) end).
{514,false}
38> timer:tc(fun() -> try Module:not_a_function() catch error:undef ->  not_defined end end).
{47,not_defined}
39> timer:tc(fun() -> erlang:function_exported(Module, not_a_function, 1) end).
{8,false}


The reason I'm asking is because I saw this gen_server:try_dispatch/3 implementation and I am wondering what are the implications under heavy load and concurrency.





On Sun, Mar 4, 2018 at 8:47 AM, Zachary Kessin <[hidden email]> wrote:
I realize that there is a race condition in that code, if someone reloads the module at the wrong moment it could crash.



Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
<a href="tel:+972%2054-234-3956" value="+972542343956" target="_blank">+972 54 234 3956 / <a href="tel:+44%2020%203734%209790" value="+442037349790" target="_blank">+44 203 734 9790 / <a href="tel:+1%20617-778-7213" value="+16177787213" target="_blank">+1 617 778 7213

On Sun, Mar 4, 2018 at 8:37 AM, Zachary Kessin <[hidden email]> wrote:
You can use the function MODULE:module_info/1 something like 
is_defined(MyFunc, Arity, MODULE)->
   Functions = MODULE:module_info(functions),
   lists:member({MyFunc,Arity}, Functions).

Where MyFunc is an atom of course

You could also expand that to a call_function_if_defined_with_default/N but i will leave that to you

Zach


Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
<a href="tel:054-234-3956" value="+972542343956" target="_blank">+972 54 234 3956 / <a href="tel:+44%2020%203734%209790" value="+442037349790" target="_blank">+44 203 734 9790 / <a href="tel:+1%20617-778-7213" value="+16177787213" target="_blank">+1 617 778 7213

On Sat, Mar 3, 2018 at 9:42 PM, Metin Akat <[hidden email]> wrote:
I'm looking into this code: https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L631

Seems like this try/catch clause is implemented with the idea that most of the time it will succeed, as the callback module will have implemented the corresponding callback function, so we will not have to go into the exception clause where it needs to call erlang:function_exported/2 one more time (presumably as an additional safeguard) which is more expensive than an optimistic try clause.

I am writing something very similar, where such a check (is a function exported) will be performed by thousands/millions of processes on every message, but in my case... it's quite probable that most of the time this WILL raise the exception. In my case the function will be purely optional - if defined, it will return a value. If not, a default value will be used. 

So my question is, how bad is this? I will test, but I'm still interested if someone has any ideas (maybe do something completely different).

Other possibilities are:

- Actually make the callback compulsory. I don't want to do that, as the problem I'm solving with this project is "See, you don't have to implement this boilerplate every time any more"

-  Put the option in application env where the user can configure it on startup. 



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions





_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Raimo Niskanen-2
On Sun, Mar 04, 2018 at 09:09:06AM +0200, Metin Akat wrote:

> I haven't yet done rigorous testing with many processes, but what you are
> suggesting is the slowest so far (at least by the most naive way of
> measuring, while not under load) :
>
> 37> timer:tc(fun() -> lists:member({not_a_function, 1},
> Module:module_info(exports)) end).
> {514,false}
> 38> timer:tc(fun() -> try Module:not_a_function() catch error:undef ->
>  not_defined end end).
> {47,not_defined}
> 39> timer:tc(fun() -> erlang:function_exported(Module, not_a_function, 1)
> end).
> {8,false}

That should be the fastest way I know of.

But still, if you call the function right after that, you still have a race
since the module may be reloaded/unloaded in between.

gen_* ignores this since it assumes that you should reload callback code as
part of an application upgrade and therefore a module should not be
reloaded/unloaded at any other time than when the server is blocked in sys:

If you can make similar assumptions the fastest way should be
erlang:function_exported/3 since that just looks up the function as it
would have done for making a call.

If you still would have to wrap the call in a try...catch you might as well
use it to detect the undefined function.

Also, please measure: try...catch is actually fairly fast.

/ Raimo



>
>
> The reason I'm asking is because I saw this gen_server:try_dispatch/3
> implementation and I am wondering what are the implications under heavy
> load and concurrency.
>
>
>
>
>
> On Sun, Mar 4, 2018 at 8:47 AM, Zachary Kessin <[hidden email]> wrote:
>
> > I realize that there is a race condition in that code, if someone reloads
> > the module at the wrong moment it could crash.
> >
> >
> >
> > Zach Kessin - CEO Finch Software
> > I boost sales with retail chatbots for fashion and cosmetics
> > +972 54 234 3956 <+972%2054-234-3956> / +44 203 734 9790
> > <+44%2020%203734%209790> / +1 617 778 7213 <+1%20617-778-7213>
> > Book a meeting with me <https://calendly.com/zkessin/chatbot>
> >
> > On Sun, Mar 4, 2018 at 8:37 AM, Zachary Kessin <[hidden email]> wrote:
> >
> >> You can use the function MODULE:module_info/1 something like
> >> is_defined(MyFunc, Arity, MODULE)->
> >>    Functions = MODULE:module_info(functions),
> >>    lists:member({MyFunc,Arity}, Functions).
> >>
> >> Where MyFunc is an atom of course
> >>
> >> You could also expand that to a call_function_if_defined_with_default/N
> >> but i will leave that to you
> >>
> >> Zach
> >>
> >>
> >> Zach Kessin - CEO Finch Software
> >> I boost sales with retail chatbots for fashion and cosmetics
> >> +972 54 234 3956 <054-234-3956> / +44 203 734 9790
> >> <+44%2020%203734%209790> / +1 617 778 7213 <+1%20617-778-7213>
> >> Book a meeting with me <https://calendly.com/zkessin/chatbot>
> >>
> >> On Sat, Mar 3, 2018 at 9:42 PM, Metin Akat <[hidden email]> wrote:
> >>
> >>> I'm looking into this code: https://github.com/erlan
> >>> g/otp/blob/master/lib/stdlib/src/gen_server.erl#L631
> >>>
> >>> Seems like this try/catch clause is implemented with the idea that most
> >>> of the time it will succeed, as the callback module will have implemented
> >>> the corresponding callback function, so we will not have to go into the
> >>> exception clause where it needs to call erlang:function_exported/2 one more
> >>> time (presumably as an additional safeguard) which is more expensive than
> >>> an optimistic try clause.
> >>>
> >>> I am writing something very similar, where such a check (is a function
> >>> exported) will be performed by thousands/millions of processes on every
> >>> message, but in my case... it's quite probable that most of the time this
> >>> WILL raise the exception. In my case the function will be purely optional -
> >>> if defined, it will return a value. If not, a default value will be used.
> >>>
> >>> So my question is, how bad is this? I will test, but I'm still
> >>> interested if someone has any ideas (maybe do something completely
> >>> different).
> >>>
> >>> Other possibilities are:
> >>>
> >>> - Actually make the callback compulsory. I don't want to do that, as the
> >>> problem I'm solving with this project is "See, you don't have to implement
> >>> this boilerplate every time any more"
> >>>
> >>> -  Put the option in application env where the user can configure it on
> >>> startup.
> >>>
> >>>
> >>>
> >>> _______________________________________________
> >>> erlang-questions mailing list
> >>> [hidden email]
> >>> http://erlang.org/mailman/listinfo/erlang-questions
> >>>
> >>>
> >>
> >

> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions


--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Jesper Louis Andersen-2
In reply to this post by Metin Akat
On Sun, Mar 4, 2018 at 8:09 AM Metin Akat <[hidden email]> wrote:
I haven't yet done rigorous testing with many processes, but what you are suggesting is the slowest so far (at least by the most naive way of measuring, while not under load) :

37> timer:tc(fun() -> lists:member({not_a_function, 1}, Module:module_info(exports)) end).
{514,false}
38> timer:tc(fun() -> try Module:not_a_function() catch error:undef ->  not_defined end end).
{47,not_defined}
39> timer:tc(fun() -> erlang:function_exported(Module, not_a_function, 1) end).
{8,false}


Having looked a bit at this, the function in (38) is interpreted, not compiled. We can try this with eministat:

-module(function_exported).

-export([t/0, datasets/0]).

-define(ROUNDS, 10000).


v1() ->
    v1(?ROUNDS).

v1(0) -> ok;
v1(N) ->
    lists:member({not_a_function, 1}, ?MODULE:module_info(exports)),
    v1(N-1).

v2() ->
    v2(?ROUNDS).

v2(0) -> ok;
v2(N) ->
    try ?MODULE:not_a_function()
    catch
        error:undef ->
            not_defined
    end,
    v2(N-1).

v3() ->
    v3(?ROUNDS).

v3(0) -> ok;
v3(N) ->
    _ = erlang:function_exported(?MODULE, not_a_function, 1),
    v3(N-1).

datasets() ->
    [eministat:s("Variant 1",
                 fun v1/0, 50),
     eministat:s("Variant 2",
                 fun v2/0, 50),
     eministat:s("Variant 3",
                 fun v3/0, 50)].

t() ->
    [H|T] = datasets(),
    eministat:x(95.0, H, T).


Running this yields:

2> function_exported:t().
x Variant 1
+ Variant 2
* Variant 3
+--------------------------------------------------------------------------+
|*          ++++++++                         x xxxxxxxxxxxxxx             x|
|*           ++++++                            x xxxxxxxx x                |
|*           ++++                              x x xxxx x x                |
|*           ++++                                  xxxx x x                |
|*           +++                                   xxx    x                |
|*           +++                                    xx    x                |
|*           +++                                    x     x                |
|*           +++                                    x                      |
|*           +++                                                           |
|*            ++                                                           |
|*            ++                                                           |
|*            ++                                                           |
|*            ++                                                           |
|*            +                                                            |
|*            +                                                            |
|*            +                                                            |
|*            +                                                            |
|*            +                                                            |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|*                                                                         |
|                                                |___MA___|                |
|            |MA|                                                          |
|A                                                                         |
+--------------------------------------------------------------------------+
Dataset: x N=50 CI=95.0000
Statistic     Value     [         Bias] (Bootstrapped LB‥UB)
Min:         1.46008e+5
1st Qu.      1.67652e+5
Median:      1.73901e+5
3rd Qu.      1.83833e+5
Max:         2.43860e+5
Average:     1.75521e+5 [      18.3119] (   1.71931e+5 ‥    1.80582e+5)
Std. Dev:    1.54033e+4 [     -419.011] (   1.12376e+4 ‥    2.45340e+4)

Outliers: 0/1 = 1 (μ=1.75539e+5, σ=1.49843e+4)
        Outlier variance:      0.604402 (severe, the data set is probably unusable)

------

Dataset: + N=50 CI=95.0000
Statistic     Value     [         Bias] (Bootstrapped LB‥UB)
Min:         3.74350e+4
1st Qu.      4.25210e+4
Median:      4.50860e+4
3rd Qu.      4.73290e+4
Max:         5.90190e+4
Average:     4.55904e+4 [     -12.9348] (   4.44757e+4 ‥    4.70025e+4)
Std. Dev:       4608.27 [     -81.7125] (      3694.90 ‥       5833.19)

Outliers: 0/3 = 3 (μ=4.55775e+4, σ=4526.56)
        Outlier variance:      0.665857 (severe, the data set is probably unusable)

Difference at 95.0% confidence
        -1.29931e+5 ± 4511.12
        -74.0257% ± 2.57013%
        (Student's t, pooled s = 1.13687e+4)
------

Dataset: * N=50 CI=95.0000
Statistic     Value     [         Bias] (Bootstrapped LB‥UB)
Min:            361.000
1st Qu.         365.000
Median:         367.000
3rd Qu.         369.000
Max:            848.000
Average:        386.060 [   1.53180e-2] (      372.960 ‥       423.160)
Std. Dev:       74.0183 [     -7.40628] (      26.8655 ‥       157.196)

Outliers: 0/6 = 6 (μ=386.075, σ=66.6120)
        Outlier variance:      0.873417 (severe, the data set is probably unusable)

Difference at 95.0% confidence
        -1.75135e+5 ± 4321.90
        -99.7800% ± 2.46232%
        (Student's t, pooled s = 1.08919e+4)
------

ok

So variant 2 is roughly 74% faster than variant 1.
And variant 3 is roughly 99.8% faster than variant 2.

I'll recommend variant 3. But you also have to measure more than a quick synthetic benchmark. Chances are that your other parts of the code is going to dominate to the point where wondering about this corner is futile.



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Richard Carlsson-3
In reply to this post by Metin Akat
You also need to consider whether or not you want loading to be automatically triggered by this check, or if you indend to ensure in some other way that the modules you are checking are known to be loaded already. Using "try Module:Function() catch ... end", if the function is not found it will cause a trap to the error handler module which will first try to load the module if it is not currently in memory. If you use only erlang:function_exported/3, it will return "false" if the module is not yet loaded.

If you assume that modules are usually loaded when you run the test, but you'd like to load them automatically if needed, you can write a function like this:

is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).

If it should be an error if the module cannot be loaded, you can add a check that ensure_loaded(M) returns {module, M} and not {error,_}.

If you want to prevent repeated attempts to load a module that failed to load the first time (since such attempted loads are slow), you could add some kind of memoization, or even generate a dummy module containing no functions, that you load instead so that subsequent calls return quickly.

        /Richard

2018-03-04 8:09 GMT+01:00 Metin Akat <[hidden email]>:
I haven't yet done rigorous testing with many processes, but what you are suggesting is the slowest so far (at least by the most naive way of measuring, while not under load) :

37> timer:tc(fun() -> lists:member({not_a_function, 1}, Module:module_info(exports)) end).
{514,false}
38> timer:tc(fun() -> try Module:not_a_function() catch error:undef ->  not_defined end end).
{47,not_defined}
39> timer:tc(fun() -> erlang:function_exported(Module, not_a_function, 1) end).
{8,false}


The reason I'm asking is because I saw this gen_server:try_dispatch/3 implementation and I am wondering what are the implications under heavy load and concurrency.





On Sun, Mar 4, 2018 at 8:47 AM, Zachary Kessin <[hidden email]> wrote:
I realize that there is a race condition in that code, if someone reloads the module at the wrong moment it could crash.



Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
<a href="tel:+972%2054-234-3956" value="+972542343956" target="_blank">+972 54 234 3956 / <a href="tel:+44%2020%203734%209790" value="+442037349790" target="_blank">+44 203 734 9790 / <a href="tel:+1%20617-778-7213" value="+16177787213" target="_blank">+1 617 778 7213

On Sun, Mar 4, 2018 at 8:37 AM, Zachary Kessin <[hidden email]> wrote:
You can use the function MODULE:module_info/1 something like 
is_defined(MyFunc, Arity, MODULE)->
   Functions = MODULE:module_info(functions),
   lists:member({MyFunc,Arity}, Functions).

Where MyFunc is an atom of course

You could also expand that to a call_function_if_defined_with_default/N but i will leave that to you

Zach


Zach Kessin - CEO Finch Software
I boost sales with retail chatbots for fashion and cosmetics
<a href="tel:054-234-3956" value="+972542343956" target="_blank">+972 54 234 3956 / <a href="tel:+44%2020%203734%209790" value="+442037349790" target="_blank">+44 203 734 9790 / <a href="tel:+1%20617-778-7213" value="+16177787213" target="_blank">+1 617 778 7213

On Sat, Mar 3, 2018 at 9:42 PM, Metin Akat <[hidden email]> wrote:
I'm looking into this code: https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L631

Seems like this try/catch clause is implemented with the idea that most of the time it will succeed, as the callback module will have implemented the corresponding callback function, so we will not have to go into the exception clause where it needs to call erlang:function_exported/2 one more time (presumably as an additional safeguard) which is more expensive than an optimistic try clause.

I am writing something very similar, where such a check (is a function exported) will be performed by thousands/millions of processes on every message, but in my case... it's quite probable that most of the time this WILL raise the exception. In my case the function will be purely optional - if defined, it will return a value. If not, a default value will be used. 

So my question is, how bad is this? I will test, but I'm still interested if someone has any ideas (maybe do something completely different).

Other possibilities are:

- Actually make the callback compulsory. I don't want to do that, as the problem I'm solving with this project is "See, you don't have to implement this boilerplate every time any more"

-  Put the option in application env where the user can configure it on startup. 



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions





_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Michał Muskała
On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:


is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.

Michał.

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Richard Carlsson-3
I was actually considering submitting such a patch a while back, but one argument against making that short-cut could be that operations against the code server should be serialized. On the other hand I haven't yet come up with an example where the short-cut would make a difference that couldn't already happen due to processor scheduling.


        /Richard

2018-03-06 16:40 GMT+01:00 Michał Muskała <[hidden email]>:
On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:


is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.

Michał.


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Tristan Sloughter-4
Ooh, that looks similar, but better, to what we did in Erleans:

    erlang:function_exported(Module, module_info, 0) orelse code:ensure_loaded(Module),
    case erlang:function_exported(Module, FunctionName, Arity) of
        true ->
            erlang:apply(Module, FunctionName, Args);
        false ->
            Default
    end.

I guess we should likely switch to `erlang:module_loaded(M)` instead of checking if `module_info` is exported. Thanks!

Would be nice to have it patched though :)

--
  Tristan Sloughter
  "I am not a crackpot" - Abe Simpson


On Tue, Mar 6, 2018, at 12:23 PM, Richard Carlsson wrote:
I was actually considering submitting such a patch a while back, but one argument against making that short-cut could be that operations against the code server should be serialized. On the other hand I haven't yet come up with an example where the short-cut would make a difference that couldn't already happen due to processor scheduling.


        /Richard

2018-03-06 16:40 GMT+01:00 Michał Muskała <[hidden email]>:

On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:



is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.


Michał.


_______________________________________________
erlang-questions mailing list


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Metin Akat
Thanks all. I went with the solution Richard proposed. And after testing of the full implementation, this check is certainly not the bottleneck of the system, as Jesper suggested.

On Tue, Mar 6, 2018 at 10:35 PM, Tristan Sloughter <[hidden email]> wrote:
Ooh, that looks similar, but better, to what we did in Erleans:

    erlang:function_exported(Module, module_info, 0) orelse code:ensure_loaded(Module),
    case erlang:function_exported(Module, FunctionName, Arity) of
        true ->
            erlang:apply(Module, FunctionName, Args);
        false ->
            Default
    end.

I guess we should likely switch to `erlang:module_loaded(M)` instead of checking if `module_info` is exported. Thanks!

Would be nice to have it patched though :)

--
  Tristan Sloughter
  "I am not a crackpot" - Abe Simpson


On Tue, Mar 6, 2018, at 12:23 PM, Richard Carlsson wrote:
I was actually considering submitting such a patch a while back, but one argument against making that short-cut could be that operations against the code server should be serialized. On the other hand I haven't yet come up with an example where the short-cut would make a difference that couldn't already happen due to processor scheduling.


        /Richard

2018-03-06 16:40 GMT+01:00 Michał Muskała <[hidden email]>:

On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:



is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.


Michał.


_______________________________________________
erlang-questions mailing list


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Michael Truog
On 03/06/2018 10:54 PM, Metin Akat wrote:
Thanks all. I went with the solution Richard proposed. And after testing of the full implementation, this check is certainly not the bottleneck of the system, as Jesper suggested.

The only problem with that, is that the documentation specifies that erlang:module_loaded/1 is only really meant to be used by the code server (at http://erlang.org/doc/man/erlang.html#module_loaded-1).  If that bothers you, you should use the function code:is_loaded/1 instead.




On Tue, Mar 6, 2018, at 12:23 PM, Richard Carlsson wrote:
I was actually considering submitting such a patch a while back, but one argument against making that short-cut could be that operations against the code server should be serialized. On the other hand I haven't yet come up with an example where the short-cut would make a difference that couldn't already happen due to processor scheduling.


        /Richard

2018-03-06 16:40 GMT+01:00 Michał Muskała <[hidden email]>:

On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:



is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.


Michał.


_______________________________________________
erlang-questions mailing list


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions




_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Richard Carlsson-3
Using code:is_loaded/1 has the same roundtrip overhead as code:ensure_loaded(), so in that case you can just use the latter directly without any extra check.

But as I see it, the warning should just be seen as a pointer for average users to primarily use the code module, and not as a warning about this function possibly going away or being bad for you, as long as you know what you are using it for.


        /Richard

2018-03-07 8:24 GMT+01:00 Michael Truog <[hidden email]>:
On 03/06/2018 10:54 PM, Metin Akat wrote:
Thanks all. I went with the solution Richard proposed. And after testing of the full implementation, this check is certainly not the bottleneck of the system, as Jesper suggested.

The only problem with that, is that the documentation specifies that erlang:module_loaded/1 is only really meant to be used by the code server (at http://erlang.org/doc/man/erlang.html#module_loaded-1).  If that bothers you, you should use the function code:is_loaded/1 instead.





On Tue, Mar 6, 2018, at 12:23 PM, Richard Carlsson wrote:
I was actually considering submitting such a patch a while back, but one argument against making that short-cut could be that operations against the code server should be serialized. On the other hand I haven't yet come up with an example where the short-cut would make a difference that couldn't already happen due to processor scheduling.


        /Richard

2018-03-06 16:40 GMT+01:00 Michał Muskała <[hidden email]>:

On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:



is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.


Michał.


_______________________________________________
erlang-questions mailing list


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions




_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: What is the fastest way to check if a function is defined in a module?

Michael Truog
On 03/07/2018 12:43 AM, Richard Carlsson wrote:
Using code:is_loaded/1 has the same roundtrip overhead as code:ensure_loaded(), so in that case you can just use the latter directly without any extra check.

But as I see it, the warning should just be seen as a pointer for average users to primarily use the code module, and not as a warning about this function possibly going away or being bad for you, as long as you know what you are using it for.

If that really is the case with erlang:module_loaded/1, then it would be best to updated the documentation so the warning is less severe.  That would make it clear that the function isn't a maintenance hazard in the future, because it may disappear or change in the future.  The warning currently says "This BIF is intended for the code server (see code(3)) and is not to be used elsewhere." which is similar to erlang:purge_module/1, erlang:delete_module/1, and erlang:load_module/2.  So the same change to the documentation should occur with these functions too.




        /Richard

2018-03-07 8:24 GMT+01:00 Michael Truog <[hidden email]>:
On 03/06/2018 10:54 PM, Metin Akat wrote:
Thanks all. I went with the solution Richard proposed. And after testing of the full implementation, this check is certainly not the bottleneck of the system, as Jesper suggested.

The only problem with that, is that the documentation specifies that erlang:module_loaded/1 is only really meant to be used by the code server (at http://erlang.org/doc/man/erlang.html#module_loaded-1).  If that bothers you, you should use the function code:is_loaded/1 instead.





On Tue, Mar 6, 2018, at 12:23 PM, Richard Carlsson wrote:
I was actually considering submitting such a patch a while back, but one argument against making that short-cut could be that operations against the code server should be serialized. On the other hand I haven't yet come up with an example where the short-cut would make a difference that couldn't already happen due to processor scheduling.


        /Richard

2018-03-06 16:40 GMT+01:00 Michał Muskała <[hidden email]>:

On 6 Mar 2018, 16:36 +0100, Richard Carlsson <[hidden email]>, wrote:



is_exported(M, F, A) ->
  case erlang:module_loaded(M) of
    false -> code:ensure_loaded(M);
    true -> ok
  end,
  erlang:function_exported(M, F, A).

(code:ensure_loaded() is slow compared to the fast call to erlang:module_loaded(), even if the module is already in memory).
 

I was recently wondering about this. Is there a reason code:ensure_loaded() does not short circuit using erlang:module_loaded()? It could probably even skip the request to the code server entirely in case the module is already loaded.


Michał.


_______________________________________________
erlang-questions mailing list


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions




_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions




_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions