在编程二郎本书的一章“编程多核CPU”,乔·阿姆斯特朗给出了一个地图功能的并行化的一个很好的例子:如何优化Erlang中数千条消息的接收循环?
pmap(F, L) ->
S = self(),
%% make_ref() returns a unique reference
%% we'll match on this later
Ref = erlang:make_ref(),
Pids = map(fun(I) ->
spawn(fun() -> do_f(S, Ref, F, I) end)
end, L),
%% gather the results
gather(Pids, Ref).
do_f(Parent, Ref, F, I) ->
Parent ! {self(), Ref, (catch F(I))}.
gather([Pid|T], Ref) ->
receive
{Pid, Ref, Ret} -> [Ret|gather(T, Ref)]
end;
gather([], _) ->
[].
它工作得很好,但我相信这是它的一个瓶颈造成它在超过100,000个元素的列表上工作的速度非常慢。
当执行gather()
函数时,它将开始将Pids
列表中的第一个Pid
与主进程邮箱中的邮件进行匹配。但是如果邮箱中最旧的邮件不是来自这个Pid
?然后它会尝试所有其他消息直到找到匹配。也就是说,在执行gather()
函数时,我们必须遍历所有邮箱消息才能找到与我们从Pids
列表中取得的Pid
匹配的一个概率。这是N * N最坏的情况下尺寸为N的列表
我甚至设法证明这个瓶颈的存在:
gather([Pid|T], Ref) ->
receive
{Pid, Ref, Ret} -> [Ret|gather(T, Ref)];
%% Here it is:
Other -> io:format("The oldest message in the mailbox (~w) did not match with Pid ~w~n", [Other,Pid])
end;
我怎样才能避免这种瓶颈?
似乎是一个非常简单的问题,惊讶还是有对此没有很好的答案。 – cnst