我是Erlang的新手,所以对于培训我尝试从头开始实现标准功能。我试图从列表模块创建并行实现map / 2 功能。但我的实施工作非常缓慢。如果我在实施中犯了任何重大错误,你能指出我吗?
-module( my_pmap ).
-export([ pmap/2 ]).
-export([ map/4, collect/3 ]).
map( F, Value, Indx, SenderPid ) ->
SenderPid ! { Indx, F( Value ) }.
pmap( F, List ) ->
CollectorPid = spawn_link( my_pmap, collect, [ length( List ), [], self() ] ),
lists:foldl(
fun( X, Indx ) ->
spawn_link( my_pmap, map, [ F, X, Indx, CollectorPid ] ),
Indx + 1
end,
1,
List ),
Mapped =
receive
{ collected, M } ->
M
end,
Sorted = lists:sort(
fun( { Indx1, _ }, { Indx2, _ } ) ->
Indx1 < Indx2
end,
Mapped ),
[ Val || { _Indx, Val } <- Sorted ].
collect( 0, List, SenderPid ) ->
SenderPid ! { collected, List };
collect( N, List, SenderPid ) when N > 0 ->
receive
Mapped ->
collect( N - 1, [ Mapped | List ], SenderPid )
end.
以下是测试结果:
1> c(my_pmap).
{ok,my_pmap}
2> timer:tc( my_pmap, pmap, [ fun(X) -> X*X*X*X end, lists:seq( 1, 10000 ) ] ).
{137804,
[1,16,81,256,625,1296,2401,4096,6561,10000,14641,20736,
28561,38416,50625,65536,83521,104976,130321,160000,194481,
234256,279841,331776,390625,456976,531441|...]}
3> timer:tc( lists, map, [ fun(X) -> X*X*X*X end, lists:seq( 1, 10000 ) ] ).
{44136,
[1,16,81,256,625,1296,2401,4096,6561,10000,14641,20736,
28561,38416,50625,65536,83521,104976,130321,160000,194481,
234256,279841,331776,390625,456976,531441|...]}
您可能已经看到 0,137804秒与 0,044136秒。
由于
答案 0 :(得分:5)
评论是正确的。问题是产卵过程很便宜但确实有成本。乘以三次数非常快,产生新过程的开销会影响您的表现。
将列表分区为片段并在单独的进程中处理每个片段可能会更快。如果您知道有8个核心,则可以尝试将其拆分为8个碎片。像pmap 这样的东西可以在Erlang中实现,但它不是Erlang的强项。像Haskell GHC运行时这样的系统有 sparks ,这是一种更好的细粒度并行工具。此外,像这样的乘法是SSE或GPU中的SIMD指令的明显候选者。 Erlang也没有这方面的解决方案,但GHC还有accelerate
和repa
这些用于处理这种情况的库。
另一方面,你可以通过简单地使用进程来处理一些暗示的片段,从而在Erlang中获得良好的加速。还要注意,由于通信开销,并行计算通常在低N(如10000)下表现很差。你需要更大的问题才能获得好处。