为什么简单的数字滤波器在Erlang中慢下来?

时间:2016-02-19 07:19:13

标签: erlang

这个Erlang脚本打印出从3开始的奇数。 脚本运行时会逐渐变慢。我的想法是以某种方式建立起来,以制作类似于Go的筛子示例,但即使过滤掉偶数,它已经太慢了。什么导致减速以及如何解决?

#!/usr/bin/env escript

main(_) ->
  print_nums().

generate(From) ->
  generate(From, 2).

generate(From, N) ->
  From ! N,
  generate(From, N+1).

filter(Caller, P) ->
  receive
    N -> case (N rem P) of
          0 -> filter(Caller, P);
          _ -> Caller ! N,
               filter(Caller, P)
         end
  end.

print_nums() ->
  Self = self(),
  FilterPid = spawn(fun() -> filter(Self, 2) end),
  spawn(fun() -> generate(FilterPid) end),
  print_nums_loop().

print_nums_loop() ->
  receive
    N -> io:format("~p~n", [N])
  end,
  print_nums_loop().

1 个答案:

答案 0 :(得分:3)

这只是因为您的生产者generate生成的消息比消费者filter消耗的消息更快。这是标准的无聊producer-consumer问题:-)

运行这个稍微修改过的脚本版本,它会在过滤器处理过程中每隔500条消息中打印出消息队列:

#!/usr/bin/env escript
%% -*- mode: erlang -*-
%%! -smp enable -hidden

main(_) ->
    Self = self(),
    FilterPid = spawn(fun() -> filter(Self, 2) end),
    spawn(fun() -> generate(FilterPid) end),
    main_loop().

main_loop() ->
    receive
        N -> io:format("~B~n", [N])
    end,
    main_loop().

filter(Caller, P) ->
    receive
        N when N rem P =:= 0 -> ok;
        N -> Caller ! N
    end,
    if N rem 500 =:= 0 -> print_queue_len();
       true -> ok
    end,
    filter(Caller, P).

print_queue_len() ->
    {message_queue_len, Len} = erlang:process_info(self(), message_queue_len),
    io:format("Len:~B~n", [Len]).

generate(Filter) ->
    generate(Filter, 2).

generate(Filter, N) ->
    Filter ! N,
    generate(Filter, N+1).

消息队列增长非常快,每收到一条消息,过滤器的速度就会慢下来。您需要让消费者控制生成器生成消息的速度(引入flow control)。就像在这段代码中一样:

#!/usr/bin/env escript
%% -*- mode: erlang -*-
%%! -smp enable -hidden

main(_) ->
    Self = self(),
    FilterPid = spawn(fun() -> filter(Self, 2) end),
    spawn(fun() -> generate(FilterPid) end),
    main_loop().

main_loop() ->
    receive
        N -> io:format("~B~n", [N])
    end,
    main_loop().

filter(Caller, P) ->
    receive
        {Pid, N} when N rem P =:= 0 -> ok;
        {Pid, N} -> Caller ! N
    end,
    Pid ! ok,
    filter(Caller, P).

generate(Filter) ->
    generate(Filter, 2).

generate(Filter, N) ->
    Filter ! {self(), N},
    receive
        ok -> generate(Filter, N+1)
    end.