一般来说,函数式语言中的模式匹配速度与Prolog相当或更快。我预计Erlang的性能与Prolog相比会快或慢一个数量级。由于函数式程序几乎仅仅是模式匹配,因此这是一个需要大量优化的领域。
内部通常有一个“模式匹配编译器”,它将高级模式匹配转换为较简单的检查序列,旨在最小化检查次数。
好的,第一个陷阱:“在shell中引入的任何内容都是解释的”。所以我们要编译模块:
-module(z).
-compile(export_all).
%% This pattern is highly uninteresting because it only matches
%% on a single value. Any decent system can do this quickly.
cl(0) -> 0;
cl(1) -> 0;
cl(2) -> 0;
cl(3) -> 0;
cl(4) -> 0;
cl(5) -> 0;
cl(6) -> 0;
cl(7) -> 0;
cl(8) -> 0;
cl(9) -> 0.
mcl(L) ->
[cl(E) || E <- L].
这为我们提供了一个你的示例运行器。
cl2(a, 0) -> a0;
cl2(a, 1) -> a1;
cl2(a, 2) -> a2;
cl2(b, 0) -> b0;
cl2(b, 1) -> b1;
cl2(b, 2) -> b2;
cl2(c, 0) -> c0;
cl2(c, 1) -> c0;
cl2(c, 2) -> c0.
mcl2(L) ->
[cl2(A, V) || {A, V} <- L].
一个更有趣示例的运行程序。在这里,我们可以利用模式中的“跳过”特性。如果在a上
(a, 0)
匹配失败,我们就知道可以直接跳到案例
(b, 0)
,因为负匹配信息可以作为系统中的信息传播。模式匹配编译器通常会进行此优化。
test1() ->
L1 = [X rem 10 || X <- lists:seq(1, 2000000)],
timer:tc(z, mcl, [L1]).
您必须自己运行此示例并评估是否足够快以满足您的使用情况。请注意,映射代码中也会花费一些时间,而且相当多的时间必须花费在通过CPU缓存从主内存中提取数据并进入CPU中。
test2() ->
random:seed(erlang:now()),
L2 = [{case random:uniform(3) of
1 -> a;
2 -> b;
3 -> c
end, V rem 3} || V <- lists:seq(1, 2000000)],
%% With HiPE this is 220937
%% Without HiPE this is 296980
timer:tc(z, mcl2, [L2]).
这个示例自然会更慢,因为我们需要匹配更多的数据才能找到匹配项。但这是一个更有趣的案例,因为它提供了一些关于匹配编译器实际速度的指示。
尝试过并行版本,但在这种情况下它们大约比实际处理慢10倍,因为创建200万个工作进程的开销远远超过了实际处理的时间 :)