Erlang 环形基准测试程序
来源:互联网 发布:js 不等于 编辑:程序博客网 时间:2024/06/08 13:13
题目来源于《Erlang 程序设计》(人民邮电出版社 ISBN 978-7-115-18869-4)第 115 页
的习题二。题目是在一个环中创建 N 个进程,然后沿着环发送一条消息 M 遍,共 N*M 遍,
统计消耗时间。
题目其实有某些不太清楚的地方,在于发送消息的方式。有以下理解方式:
1)进程1向进程2发送消息,进程2收到后再向进程3发送消息,此外的时间进程2
处于等待状态,其余进程依次类推;
2)上一进程直接向下一进程发送 M 次消息,别的一概不管,也不等待;
其实 1)中的理解有个小问题:谁来启动这个发送过程呢?大家都在等待啊。(当然,
可以采用一个额外的函数执行这一过程)好了,闲话不多说,我的个人理解是第二种,就此
开始。
首先需要考虑的问题是怎样得到下一进程的 Pid,要知道,根据 107 页上的例子,可以
通过生成一个包含 N 个 Pid 的列表来得到 N 个进程。那么怎样知道我的下一个进程的 Pid
是多少呢?不知道的话怎么给人家发送信息啊。我的解决办法是将这个 Pid 列表传递给进程,
让进程找到自己的下一个进程的 Pid。
好,这样一来引入一个新的问题,怎样得到下一进程的 Pid?我们需要一个判断自己的
Pid 在进程列表中的位置的函数。我的实现如下:
%%判断 Element 在 L 中的位置的函数
judge(Element,[_H|T]) when Element=:=_H -> length(T);
judge(Element,[_H|T]) -> judge(Element,T).
需要说明的是,我返回的不是 Element 在列表中的位置,而是列表从 Element 以后尾
部(Tail)的长度。我们当然可以使 judge/2 这个函数直接返回 Element 在列表中的位置,
只需要将函数的第一行的 length(T)改为 length([_H|T])-length(T)即可,但是这样会增加一
次 length 这个 BIF 的调用。一次的增大倒是问题不大,可是对于上万上十万的进程,还是
值得考虑的。既然咱们是测试运行时间的,就该避免这样的行为,这叫消除误差。
由 judge/2 返回尾部的长度,而 L 的长度为 N 是已知的,咱们只需要把 N 传递给每个
进程就好了,如下:
%%将 Pid 列表传递给每个进程
lists:foreach(fun(Pid) -> Pid!{Lew,N,M} end,L),
这里的 Lew 是:
Lew=lists:append(L,[lists:nth(1,L)]),
将 l 的第一个元素加在 L 后面构成新的 Pid 列表 Lew,再发送给每个进程,这是让进程
成为环形的技巧。如下图所示:
进程1 进程2 进程3 ●●● 进程 N
列表 L 的结构
进程1 进程2 进程3 ●●● 进程 N 进程1
列表 Lew 的结构
由此,每个进程只需要向列表 Lew 的下一进程发送消息即可实现环形发送消息。
接下来的问题是怎样发送消息 M 遍。再次利用 lists 模块中的 foreach 函数,用 lists 中
的 duplicate 函数构造一个包含 M 个 NextPid 的列表,然后利用 foreach 函数向下一进程
发送 M 次消息。实现如下:
%%发送消息 M 次
lists:foreach(fun(Pid) -> Pid!{hello} end,lists:duplicate(M,NextPid)),
至此,主要的问题完全解决,完整代码附在最后。
最后一个有意思的小问题,是关于每个进程退出的问题。如果采取向下一进程发送消息
{die}来使之退出(就像注释中写的那样),可能会出现本进程被上一进程结束而来不及向下
一进程发送退出信息,导致下一进程幸存下来。如果将%%NextPid!{die}, 一句恢复而将主
函数中倒数第二句注释掉,就可以测试下。我测试的结果是每次总有两个进程能存活下来。
运行 shell 的时候用 pman:start()命令打开进程管理器,就能看到上万的进程像洪水一样消
长的壮观景象,同时最后也能看到残留下来的两个进程。
-module(ring_test).
-compile(export_all).
judge(Element,[_H|T]) when Element=:=_H -> %%判断Element在L中的位置的函数
length(T);
judge(Element,[_H|T]) ->
judge(Element,T).
for(Num,Num,F) ->[F()]; %%类似for循环的函数
for(I,Num,F) ->[F()|for(I+1,Num,F)].
subfun()-> %%收发信息的子函数
receive
{List,N,M} ->
NextPid=lists:nth(N+2-judge(self(),List),List),
lists:foreach(fun(Pid) -> Pid!{hello} end,lists:duplicate(M,NextPid)),
%%发送消息M次
%%NextPid!{die}, %%若如此则可能出现幸存下的进程
subfun();
{hello} ->
%io:format("ok,I got it!~n"),
subfun();
{die} ->
void
end.
main_fun(N,M) ->
L=for(1,N,fun() -> spawn(ring_test,subfun,[]) end),
Lew=lists:append(L,[lists:nth(1,L)]), %%将L的第一个元素加在L后成Lew
lists:foreach(fun(Pid) -> Pid!{Lew,N,M} end,L), %%
lists:foreach(fun(Pid) -> Pid!{die} end, L). %%向每个进程发出退出信号
time(N,M) ->
{Rtime,_}=timer:tc(judge,main_fun,[N,M]),
io:format("The realtime is ~p seconds~n",[Rtime/1000000]).
- Erlang 环形基准测试程序
- Erlang 环形基准测试
- Erlang信息传递环形基准测试
- Erlang环形基准测试 (练习)
- 基准测试程序
- 基准测试
- 高性能计算基准测试程序(二)--MPI测试基准
- 目前高性能计算基准测试程序分类
- tpc-c 基准测试简介以及程序的安装、使用
- 目前高性能计算基准测试程序分类
- 高性能计算基准测试程序(一)--NPB
- Erlang程序的性能测试工具(1)
- Java基准测试
- hadoop基准测试
- Hadoop Gridmix基准测试
- 运行hadoop基准测试
- 基准测试工具
- hadoop基准测试---新手入门
- windows下系统dll文件大全
- Varchar与char的区别
- Installation von SubVersion
- set default path of linux on msst.cc
- C++类对象的拷贝构造函数
- Erlang 环形基准测试程序
- 各种技术网站的网址
- Silverlight中引用wcf出现“给定关键字不在字典中”的错误
- 10+ Useful JavaScript Regular Expression Functions to improve your web applications efficiency
- 第10章 关联容器
- 深入浅出Attribute
- C#的多线程机制探索
- 常用正则表达式大全!(例如:匹配中文、匹配html)
- [原创] 活力(智力情绪体力)周期测试脚本 (bash脚本)