Erlang中的并行深度优先搜索比其顺序对应慢

时间:2011-11-22 13:24:17

标签: erlang parallel-processing depth-first-search

我正在尝试在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 *;
  • 继续运行* 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导致这种缓慢?
  • 我在列表中累积当前路径并将其传递给每个新生成的* dfs_mod *进程,可能会传递这么多列表是什么问题?
  • 每次访问新顶点并将其添加到路径时,我都会使用ETS表来保存路径。 ETS属性是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线程都忙。

1 个答案:

答案 0 :(得分:8)

没有代码就没有快速的方法可以知道,但这里有一个快速列表,说明为什么会失败:

  • 您可能会混淆并行性和并发性。 Erlang的模型是无共享的,并且首先针对并发性(独立运行不同的代码单元)。并行性只是对此的优化(同时运行一些代码单元)。通常,并行性将采用更高级别的形式,比如你想在50种不同的结构上运行排序功能 - 然后你决定运行50个顺序排序函数。

  • 您可能遇到同步问题或顺序瓶颈,有效地将并行解决方案更改为顺序解决方案。

  • 复制数据,上下文切换等方面的开销使得并行性方面的收益相形见绌。前者尤其适用于大数据集,您可以分解为子数据集,然后再加入大数据集。后者尤其适用于高度顺序的代码,如过程环基准所示。

如果我想优化它,我会尝试将消息传递和数据复制减少到最低限度。

如果我是那个人,我会保留顺序版本。它完成它应该做的事情,并且当一个更大的系统的一部分,只要你有比核心更多的进程,并行性将来自对sort函数的多次调用而不是sort函数的分支。从长远来看,如果服务器或服务的一部分,使用顺序版本N次应该没有比并行的更多负面影响,最终创建许多,更多的进程来执行相同的任务,并且风险使系统更多的风险