[erlang-questions] What is the fastest way to check if a function is defined in a module?
Jesper Louis Andersen
jesper.louis.andersen@REDACTED
Tue Mar 6 16:05:10 CET 2018
On Sun, Mar 4, 2018 at 8:09 AM Metin Akat <akat.metin@REDACTED> 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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20180306/ba19a77e/attachment.htm>
More information about the erlang-questions
mailing list