我正在尝试在Erlang中实现修改后的并行深度优先搜索算法(我们称之为* dfs_mod *)。
我想要得到的只是所有'死胡同路径',它们基本上是当* dfs_mod *访问没有邻居的顶点或带有邻居的顶点时返回的路径。如果我的自定义函数ets_table1
返回fun1(Path)
,我将每个路径保存到true
,如果ets_table2
返回fun1(Path)
,我会保存到false
(我需要过滤通过一些客户过滤器产生“死胡同”路径。)
我已经实现了这个算法的顺序版本,并且由于一些奇怪的原因,它的性能优于并行版本。
并行实现背后的想法很简单:
Vertex
[Vertex|Other_vertices] = Unvisited_neighbours
Vertex
添加到当前路径; {self(), wait}
发送到'收集'流程; Unvisited_neighbours
的{{1}}运行* dfs_mod *; Vertex
); Other_vertices
发送到收集器进程并终止; 所以,基本上每当我访问一个带有未访问邻居的顶点时,我会产生一个新的深度优先搜索过程,然后继续其他顶点。
在产生第一个* dfs_mod *进程后,我开始收集所有{self(), done}
和{Pid, wait}
消息({Pid, done}
消息是为了让收集器等待所有wait
消息)。在等待后的N毫秒内,收集器函数返回done
。
出于某种原因,这个并行实现的运行时间为8到160秒,而顺序版本仅运行4秒(测试是在具有Intel i5处理器的机器上的5个顶点的完全连接的有向图上完成的。)
以下是我对这种糟糕表现的看法:
ok
传递给每个运行* dfs_mod *的新进程。也许对许多进程中的一个有向图进行Graph
导致这种缓慢?digraph:out_neighbours(Graph)
,但也许我做错了什么?([bag, public,{write_concurrency, true})
的路径(它基本上会检查路径是否在顶点之前出现标有字母“n”的顶点“ m“并根据结果返回fun1()
。也许这true/false
减慢了事情的速度?fun1()
和done
消息的情况下运行* dfs_mod *,但是* wait
在* dfs_mod *返回{{}后很长一段时间内显示了很多Erlang活动在shell中,所以我认为活动消息传递不会减慢速度。如何使我的并行dfs_mod比顺序对应的更快?
编辑:当我运行parallel * dfs_mod *时,htop
根本没有显示任何进程,尽管ok
显示所有4个CPU线程都忙。
答案 0 :(得分:8)
没有代码就没有快速的方法可以知道,但这里有一个快速列表,说明为什么会失败:
您可能会混淆并行性和并发性。 Erlang的模型是无共享的,并且首先针对并发性(独立运行不同的代码单元)。并行性只是对此的优化(同时运行一些代码单元)。通常,并行性将采用更高级别的形式,比如你想在50种不同的结构上运行排序功能 - 然后你决定运行50个顺序排序函数。
您可能遇到同步问题或顺序瓶颈,有效地将并行解决方案更改为顺序解决方案。
复制数据,上下文切换等方面的开销使得并行性方面的收益相形见绌。前者尤其适用于大数据集,您可以分解为子数据集,然后再加入大数据集。后者尤其适用于高度顺序的代码,如过程环基准所示。
如果我想优化它,我会尝试将消息传递和数据复制减少到最低限度。
如果我是那个人,我会保留顺序版本。它完成它应该做的事情,并且当一个更大的系统的一部分,只要你有比核心更多的进程,并行性将来自对sort函数的多次调用而不是sort函数的分支。从长远来看,如果服务器或服务的一部分,使用顺序版本N次应该没有比并行的更多负面影响,最终创建许多,更多的进程来执行相同的任务,并且风险使系统更多的风险