Erlang 编程/并行编程
外观
Linda 是一种协调多个处理器行动的方式。创建一个元组空间供候选素数存储。创建筛子进程并将消息发送到元组空间以移除非素数。筛子进程还相互发送消息以移除非素数筛子。
该程序可以使用多少个处理器?该程序创建的筛子数量等于矩阵(元组空间)中数字的平方根。如果我们寻找小于 100 的素数,那么大约有 10 个并行的筛子进程。实际上,大多数筛子进程都被停止,最终只剩下(小于 Max 的平方根的素数数量)个进程。这允许轻松地实现 100 的 10 个并行进程和 10000 的 100 个并行进程,只需少量修改。
在 Erlang 中,不建议使用大量原子,因此 N 不应过大。我们也可能耗尽进程或内存。
该程序违反了 Erlang 进程管理的一般规则:不要使用对等管理器。每个筛子进程都是一个对等管理器,因为每个筛子都可能停止其他任何筛子。相反,进程应该在自上而下的树形结构中进行管理。筛子的对等管理会导致一些糟糕的计时问题。计时是使用对等管理器通常是一个坏主意的一大原因。
当 2 的筛子结束时,会转储素数列表。Erlang 应该为每个进程分配相同的 CPU 时间。当时间片均匀时,2 的筛子首先开始,最后结束。当其他一些筛子因时间不足而被饿死时,它可能在 2 的筛子结束后结束,素数转储会过早,导致一些能被 2 整除的数字保留在推测素数列表中。某个进程的相对饥饿大约发生在 10 次中的一次。很明显,关键的词应该是:“尝试”。“Erlang 尝试为每个进程分配相同的时间片。”
运行非素数筛子会有害吗?我们可以使用一个 6 筛子。一个 6 筛子是冗余的,因为 2 筛子和 3 筛子会移除 6 筛子会移除的所有数字。通过移除 6 筛子及其非素数筛子兄弟,我们减少了矩阵必须处理的消息数量,从而加快了最终结果。
-module(primes). % This is a simple linda tuplespace. Here we use it to find primes numbers. % This tuplespace cannot have duplicate tuples, but with a prime sieve it does % not matter. -compile(export_all). start() -> start(100). % default value for max is 100 start(Max) -> io:format(" Loading ~w numbers into matrix (+N) \n ", [ Max ] ), Lid = spawn_link( primes, linda, [Max, [], [] ]), Sqrt = round(math:sqrt(Max)+0.5), io:format(" Sqrt(~w) + 1 = ~w \n " , [Max,Sqrt] ), io:format(" Tuple space is started ~n ",[]), io:format(" ~w sieves are spawning (+PN) ~n ", [Sqrt] ), io:format(" Non prime sieves are being halted (-PN) ~n ", [] ), % load numbers into tuplespace % and spawn sieve process spawn( primes, put_it, [Max, Max, Lid] ). start_sieves(Lid) -> Lid ! {self(), get, all, pids}, receive {lindagram, pids, Pids} -> done end, start_sieve_loop(Pids). start_sieve_loop([]) -> done; start_sieve_loop([Pid|Pids]) -> receive after 100 -> done end, Pid ! {start}, start_sieve_loop(Pids). spawn_sieves( _Max, Sqrt, _Lid, Sqrt ) -> done; spawn_sieves( Max, Inc, Lid, Sqrt ) -> T = 1000, Pid = spawn( primes, sieve, [ Max, Inc+Inc, Inc, Lid, T ]), Name = list_to_atom("P" ++ integer_to_list(Inc)), Lid ! {put, pid, Name}, register( Name, Pid), io:format(" +~s ", [atom_to_list(Name)]), spawn_sieves( Max, Inc+1, Lid, Sqrt ). put_it(Max, N, Lid) when N =< 1 -> Sqrt = round(math:sqrt(Max)+0.5), spawn_sieves( Max, 2, Lid, Sqrt ); put_it(Max, N,Lid) when N > 1 -> receive after 0 -> Lid ! {put, N, N}, if N rem 1000 == 0 -> io:format(" +~w ", [N]); true -> done end, put_it(Max, N-1,Lid) end. % the 2 sieve starts last and has the most to do so it finishes last sieve(Max, N, 2, Lid, _T) when N > Max -> io:format("final sieve ~w done, ~n", [2] ), Lid ! {dump,output}; sieve(Max, N, Inc, _Lid, _T) when N > Max -> io:format("sieve ~w done ", [Inc] ); sieve(Max, N, Inc, Lid, T) when N =< Max -> receive after T -> NT = 0 end, receive {lindagram,Number} when Number =/= undefined -> clearing_the_queue; {exit} -> exit(normal) after 1 -> done end, % remove multiple of number from tuple-space Lid ! {self(), get, N}, Some_time = (N rem 1000)==0, if Some_time -> io:format("."); true -> done end, % remove (multiple of Inc) from sieve-process space Name = list_to_atom("P" ++ integer_to_list(N)), Exists = lists:member( Name, registered()), if Exists -> Name ! {exit}, io:format(" -~s ", [atom_to_list(Name)] ); true -> done end, sieve(Max, N+Inc, Inc, Lid, NT). % next multiple %% linda is a simple tutple space %% if it receives no messages for 2 whole seconds it dumps its contents %% as output and halts linda(Max, Keys, Pids) -> receive {put, pid, Pid} -> linda(Max, Keys, Pids++[Pid]); {put, Name, Value} -> put( Name, Value), linda(Max, Keys++[Name], Pids); {From, get, Name} -> From ! {lindagram, get( Name)}, erase( Name ), % get is a destructive read linda(Max, Keys--[Name],Pids); {From, get, all, pids} -> From ! {lindagram, pids, Pids}, linda(Max, Keys, Pids ); {From, get, pid, Pid} -> L1 = length( Pids ), L2 = length( Pids -- [Pid]), if L1 > L2 -> % if it exists From ! {lindagram, pid, Pid}; true -> From ! {lindagram, pid, undefined} end, linda(Max, Keys, Pids ); {dump,output} -> io:format(" ~w final primes remain: ~w ~n ", [length(Keys), lists:sort(Keys) ]) after (100*Max) -> % if there is not tuple action after some time then print the results io:format(" ~w primes remain: ~w ~n ", [length(Keys), lists:sort(Keys) ]) end.
c(primes). primes:start(1000). Loading 1000 numbers into matrix (+N) Sqrt(1000) + 1 = 32 Tuple space is started 32 sieves are spawning (+PN) Non prime sieves are being halted (-PN) +1000 <0.46.0> +P2 +P3 +P4 +P5 +P6 +P7 +P8 +P9 +P10 +P11 +P12 +P13 +P14 +P15 +P16 +P17 +P18 +P19 +P20 +P21 +P22 +P23 +P24 +P25 +P26 +P27 +P28 +P29 +P30 +P31 -P8 -P6 -P4 -P9 -P12 -P10 -P15 -P15 -P18 -P14 -P21 -P21 -P22 -P26 -P20 -P24 -P25 -P27 -P28 -P30 -P30 -P16 sieve 31 done sieve 29 done sieve 19 done sieve 23 done sieve 11 done sieve 13 done sieve 17 done sieve 7 done .sieve 5 done sieve 3 done .final sieve 2 done, 168 final primes remain: [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67, 71,73,79,83,89,97, 101,103,107,109,113,127,131,137,139,149,151,157,163, 167,173,179,181,191,193,197,199, 211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293, 307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397, 401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491, 499,503,509,521,523,541,547,557,563,569,571,577,587,593,599, 601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691, 701,709,719,727,733,739,743,751,757,761,769,773,787,797, 809,811,821,823,827,829,839,853,857,859,863,877,881,883,887, 907,911,919,929,937,941,947,953,967,971,977,983,991,997]