4

私は Erlang を初めて使用するので、トレーニングのために標準関数をゼロから実装しようとしています。listモジュールから 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 秒。

ありがとう

4

1 に答える 1

5

コメントは正しいです。問題は、産卵プロセスは安価ですが、コストがかかることです数の 3 倍の乗算は非常に高速であり、新しいプロセスを生成するオーバーヘッドによってパフォーマンスが低下します。

リストをフラグメントに分割し、各フラグメントを個別のプロセスで処理すると、おそらく高速になります。8 つのコアがあることがわかっている場合は、それを 8 つのフラグメントに分割してみることができます。pmap のようなものは Erlang で実装できますが、Erlang の強みではありません。Haskell GHC ランタイムのようなシステムには、このような細粒度の並列処理のための優れたツールであるsparksがあります。また、そのような乗算は、SSE または GPU の SIMD 命令の明らかな候補です。Erlang にもこれに対する解決策はありませんが、GHC にはacceleraterepaこの状況を処理するためのライブラリである と があります。

一方、Erlang では、いくつかのフラグメントを処理するプロセスを使用するだけで、かなり高速化できます。また、並列計算は、通信オーバーヘッドのために、低い N (10000 など) でパフォーマンスが低下することが多いことに注意してください。利益を得るには、より大きな問題が必要です。

于 2012-08-08T09:55:16.267 に答える