Erlang实战:并行枚举排序

て烟熏妆下的殇ゞ 提交于 2020-01-25 05:13:13

  这是海量数据的时代!互联网每天产生的数据量远远超出了我们所能想象的范围,无论是国外的Facebook、Twitter,还是国内的微博、人人,还有各种电商们,这些互联网企业在数据上是富有的,它们掌握着海量的用户数据,同时它们也需要对这些海量数据进行分析和处理。我们以前的串行化算法似乎显得力不从心,一个是计算机本身就设计为多核的,它们存在并行化问题;另一方面,更多的计算机加入到并行的行列,并行化正成为一种潮流和趋势,因为它们能用空间换取时间和性能,或者说用更多的机器来进行分析计算。

  我知道的常用的并行计算工具大致有三种:MapReduce、MPI,还有就是本文讨论的Erlang。MapReduce最早是google设计并实施的,现在已经成为云计算中的一个火热的技术,它通过一个映射(Map)和规约(Reduce)来进行并行数据处理,实际上它是一个实时批处理再加上一些容错处理机制来保证系统的性能和可靠性;MPI我不太熟悉,它是一个库,不是一门语言,一般与C/FORTRAN结合使用;Erlang是一个基于消息传递的并发编程语言,正因为它是基于消息的,因此没有共享内存和锁机制,因此Erlang比较简单,它是函数型编程语言,我们在编写程序时写的都是函数。

  下面我将通过一个排序实例:枚举排序来讲述Erlang并行编程!

  实战:并行枚举排序

  我们先来看看其概念:枚举排序也叫秩排序(Rank Sort),该算法的基本思想是:对每一个要排序的元素统计小于它的所有元素的个数,从而得到该元素在整个序列中的位置。其时间复杂度为o(n^2)。枚举排序串行算法伪代码如下:

View Code
 1 输入为:a[1], a[2] , ... , a[n]
 2 
 3 输出为:b[1], b[2] , ..., b[n]
 4 
 5 for i =1 to n do
 6 
 7     1)k =1
 8 
 9     2)for j = 1 to n do
10 
11           if a[i] > a[j] then
12 
13              k= k + 1
14 
15          endif
16 
17       endfor
18 
19       3)b[k] = a[i]
20 
21 endfor

  通过将此串行化算法改造为并行算法,得到其并行算法的伪代码如下:

View Code
 1 输入无序数组为:a[1], a[2] , ... , a[n]
 2 输出有序数组为:b[1], b[2] , ..., b[n]
 3 Begin
 4 P0播送a[1], a[2] , ... , a[n]给所有Pi
 5 for all Pi where 1=<i<=n para do
 6     k =1
 7     for j = 1 to n do
 8           if (a[i] > a[j]) or (a[i]=a[j] and i>j) then
 9              k= k + 1
10          endif
11     endfor
12 endfor
13 P0收集k并按序定位
14 End

  通过将串行枚举排序算法改造为并行枚举排序算法,我们可以看到排序N个元素需要N个处理器,每个处理器(节点或进程)的时间复杂度为O(N),总的计算复杂度为O(N),但是在并行算法中,通信复杂度占据了很重要的位置,在此算法中,总的通信复杂度为O(N^2)。好了,相信你对枚举排序也有了大致的了解了,下面我将用Erlang实现并行枚举排序,由于环境所限,主要是在单机上的多个进程间来进行枚举排序。

  Erlang代码如下:

View Code
  1 %% 启动排序时,需存储各节点信息:使用store(Key,Value)方法
  2 %%然后,便可启动enum_sort(Data)进行排序
  3 -module(enum_sort).
  4 -export([start/2,enum_sort/1,store/2,lookup/1]).
  5 
  6 start(Node,Key) ->
  7     Pid = spawn(Node, fun() -> loop(Key) end).
  8     
  9 enum_sort(Data) ->
 10     register(server,self()),
 11     store(server,node()),
 12     rpc(Data),
 13     Final = merge(Data,[]),
 14     io:format("~nEnum Sorting success: ~p~n",[lists:reverse(Final)]).
 15 
 16 rpc(Data) -> 
 17     Len = length(Data) -1,
 18     send(Len,Data),
 19     Loc = enum_once(Data,Len+1),
 20     self() ! {self(),Loc}.
 21 
 22 store(Key,Value) ->
 23     put(Key,Value).
 24     
 25 lookup(Key) ->
 26     get(Key).
 27 
 28 %% 向N-1个进程发送排序数据
 29 send(1 , Data) ->
 30     Node1 = lookup(1),
 31     Pid = start(Node1,1),
 32     Pid ! {node(), self(), Data};
 33 send(N,Data) ->
 34     NodeN  = lookup(N),
 35     Pid = start(NodeN,N),
 36     Pid ! {node(), self(), Data},
 37     send(N-1,Data).
 38     
 39 genNode(N) ->
 40     Temp = lists:concat([node,N,"@dell-PC"]),
 41     Ret = list_to_atom(Temp).
 42     
 43 genAtom(N) ->
 44     Temp = lists:concat([process,N]),
 45     Ret = list_to_atom(Temp).    
 46 
 47 %%节点N准备接受排序数据
 48 loop(N) ->
 49     %io:format("1---Current node:~p~n",[node()]),
 50     receive
 51         {Node,Pid,die} ->
 52             disconnect_node(Node),
 53             io:format("Node (~p) disconnected~n",[Node]),
 54             quit;
 55         {Node,Pid,L} ->
 56             store(server,Node),
 57             Loc  = enum_once(L,N),
 58             %io:format("Come from Server Pid=~p, Node = ~p~n",[Pid,Node]),
 59             %io:format("Process Node Pid = ~p  Location=~p~n",[self(),Loc]),
 60             {server,Node} ! {node(), genAtom(N),Loc},
 61             loop(N)
 62     end.
 63     
 64 %% Pi进程运行enum_once一次,得到一个K值
 65 enum_once(L,I) ->
 66     if
 67         I =:= length(L) ->
 68             Node = lookup(server);
 69         I =/= length(L) ->
 70             Node = lookup(I)
 71     end,
 72     %io:format("Node (~p) is processing one enum~n",[node()]),
 73     Loc = lookup(L,I,1),
 74     Ele = lists:nth(I,L),
 75     io:format("Node(~p) Data[~p]= ~p Sorting loc=( ~p)~n",[node(),I,Ele,Loc]),
 76     Loc.
 77 
 78     
 79 %% 计算Pi进程,即第I个元素在有序列表中的位置
 80 lookup(L,I,J) -> 
 81     if 
 82         J > length(L) ->
 83             0;
 84         J =< length(L) ->
 85             Ai = lists:nth(I,L),
 86             Aj = lists:nth(J,L),
 87             Value1 = Ai  > Aj ,
 88             Value2 = (Ai =:= Aj) and ( I > J ),
 89             Value = Value1 or Value2,
 90             if 
 91                  Value =:= true ->
 92                     1 + lookup(L,I,J+1);
 93                  Value =:= false ->
 94                     lookup( L, I, J+1)
 95             end
 96     end.
 97 
 98 
 99 %% 总控端将收集到的K值进行整理合并
100 merge(Data,Ret) ->
101     Len  = length(Data),
102     Cur = length(Ret),
103     if Cur < Len ->
104         receive
105             {Pid, Loc} ->
106                 Pn = length(Data),
107                 Ret1 = [{ Pn, Loc } | Ret],
108                 merge(Data,Ret1);
109             {Node, Pid,Loc}  ->
110                 Process = atom_to_list(Pid),
111                 ListAfter = lists:nthtail( length("process"), Process),
112                 Pi = list_to_integer(ListAfter),
113                 Ret1 = [{ Pi ,Loc } | Ret],
114                 merge(Data,Ret1)
115         end;
116        Cur =:= Len ->
117                %% io:format("{Key, Value} = {element locations, sorting location} = (~p)~n",[Ret]),
118             sort(Data,Ret, [])
119     end.
120 
121 %% 将收集的K值,进行排序处理
122 sort( Data, [ ] ,Temp) -> Temp;
123 sort( Data, L, Temp )     -> 
124     H = lists:nth(1,L),
125     { X, Y} = H,
126     Element = lists:nth( X, Data),
127     %io:format("Element = (~p) X = (~p) Y = (~p)~n",[Element,X,Y]),
128     Len = length(Temp),
129     if Y =:= Len ->
130             Next = [ Element | Temp],
131             Filter = lists:delete( H ,L),
132             sort(Data, Filter, Next);
133        Y =/= Len ->
134                After = lists:delete(H, L),
135                %%Filter = lists:concat( After, [H]),
136                Filter = lists:append(After, [H]),
137                %% io:format("H = (~p)  After = (~p) Filter =(~p)~n",[H,After,Filter]),
138                sort( Data, Filter, Temp)
139      end.

  下面看一下程序运行结果:

  好了,我主要将枚举排序的主要过程讲了,还有Erlang实现的源码放在这了,这是以前写的,有些方面写的可能有些复杂,具体细节就不说了,如有问题,可以留言给我。我将在以后的文章中写两个关于并行快排和PSRS的排序,欢迎大家继续支持及提出宝贵意见!

  

  注:如无特殊说明,本博客内容纯属原创,转载请注明:http://www.cnblogs.com/itfreer/ | IT闲人,谢谢!

 

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!