【发布时间】:2019-10-30 04:52:03
【问题描述】:
我正在学习函数式编程和 Erlang 的基础知识,并且我已经实现了三个版本的阶乘函数:使用带保护的递归、使用带模式匹配的递归和使用尾递归。
我正在尝试比较每个阶乘实现的性能(Erlang/OTP 22 [erts-10.4.1]):
%% Simple factorial code:
fac(N) when N == 0 -> 1;
fac(N) when N > 0 -> N * fac(N - 1).
%% Using pattern matching:
fac_pattern_matching(0) -> 1;
fac_pattern_matching(N) when N > 0 -> N * fac_pattern_matching(N - 1).
%% Using tail recursion (and pattern matching):
tail_fac(N) -> tail_fac(N, 1).
tail_fac(0, Acc) -> Acc;
tail_fac(N, Acc) when N > 0 -> tail_fac(N - 1, N * Acc).
计时器助手:
-define(PRECISION, microsecond).
execution_time(M, F, A, D) ->
StartTime = erlang:system_time(?PRECISION),
Result = apply(M, F, A),
EndTime = erlang:system_time(?PRECISION),
io:format("Execution took ~p ~ps~n", [EndTime - StartTime, ?PRECISION]),
if
D =:= true -> io:format("Result is ~p~n", [Result]);
true -> ok
end
.
执行结果:
递归版本:
3> mytimer:execution_time(factorial, fac, [1000000], false).
Execution took 1253949667 microseconds
ok
带有模式匹配版本的递归:
4> mytimer:execution_time(factorial, fac_pattern_matching, [1000000], false).
Execution took 1288239853 microseconds
ok
尾递归版本:
5> mytimer:execution_time(factorial, tail_fac, [1000000], false).
Execution took 1405612434 microseconds
ok
我期待尾递归版本的性能比其他两个版本更好,但令我惊讶的是它的性能较差。这些结果与我的预期完全相反。
为什么?
【问题讨论】:
-
编写基准测试很难。你确定你正在测量你认为你正在测量的东西吗?您是否考虑了统计效应?您是否考虑了动态自适应优化?你考虑到环境了吗?以下是您需要在基准测试中考虑哪些非显而易见的事情的几个示例:groups.google.com/forum/#!msg/mechanical-sympathy/icNZJejUHfE/…、*.com/a/513259/2988。这些主要讨论的是 HotSpot,但大多数问题适用于任何具有动态自适应优化的现代高性能执行引擎。
标签: erlang tail-recursion