[erlang-questions] What is the fastest way to check if a function is defined in a module?

Raimo Niskanen raimo+erlang-questions@REDACTED
Tue Mar 6 15:29:03 CET 2018


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 <zkessin@REDACTED> 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 <zkessin@REDACTED> 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 <akat.metin@REDACTED> 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
> >>> erlang-questions@REDACTED
> >>> http://erlang.org/mailman/listinfo/erlang-questions
> >>>
> >>>
> >>
> >

> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions


-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list