【问题标题】:Parallel depth-first search in Erlang is slower than its sequential counterpartErlang 中的并行深度优先搜索比其顺序搜索慢
【发布时间】:2012-01-03 21:06:30
【问题描述】:

我正在尝试在 Erlang 中实现一种改进的并行深度优先搜索算法(我们称之为 *dfs_mod*)。

我想要得到的只是所有的“死路”,它们基本上是当 *dfs_mod* 访问没有邻居的顶点或具有已经访问过的邻居的顶点时返回的路径。如果我的自定义函数fun1(Path) 返回true,我将每个路径保存到ets_table1,如果fun1(Path) 返回false,则保存到ets_table2(我需要使用一些客户过滤器过滤生成的“死胡同”路径) .

我已经实现了这个算法的顺序版本,但出于某种奇怪的原因,它的性能比并行算法要好。

并行实现背后的想法很简单:

  • [Vertex|Other_vertices] = Unvisited_neighbours访问Vertex
  • 将此Vertex 添加到当前路径;
  • {self(), wait} 发送到“收集器”进程;
  • 新进程中为当前Vertex中的Unvisited_neighbours运行*dfs_mod*;
  • 继续运行 *dfs_mod* 与提供的其余顶点 (Other_vertices);
  • 当没有更多的顶点可以访问时 - 发送{self(), done} 到收集器进程并终止;

所以,基本上每次我访问一个带有未访问邻居的顶点时,我都会产生一个新的深度优先搜索过程,然后继续处理其他顶点。

在产生第一个 *dfs_mod* 进程后,我开始收集所有 {Pid, wait}{Pid, done} 消息(wait 消息是让收集器等待所有 done 消息)。在等待收集器函数 N 毫秒后返回ok


出于某种原因,这种并行实现的运行时间为 8 到 160 秒,而顺序版本仅运行 4 秒(测试是在配备 Intel i5 处理器的机器上对具有 5 个顶点的全连接有向图进行的)。

以下是我对如此糟糕表现的看法:

  • 我将有向图Graph 传递给每个运行*dfs_mod* 的新进程。也许对来自许多进程的一个有向图执行digraph:out_neighbours(Graph) 会导致这种缓慢?
  • 我将当前路径累积在一个列表中并将其传递给每个新生成的 *dfs_mod* 进程,也许传递这么多列表是问题所在?
  • 每次访问新顶点并将其添加到路径时,我都会使用 ETS 表来保存路径。 ETS 属性是([bag, public,{write_concurrency, true}),但也许我做错了什么?
  • 每次我访问一个新顶点并将其添加到路径时,我都会使用自定义函数fun1() 检查路径(它基本上检查路径是否有标记为字母“n”的顶点出现在带有“m”的顶点之前并根据结果返回true/false)。也许这个fun1() 会减慢速度?
  • 我尝试在不收集donewait 消息的情况下运行*dfs_mod*,但是在*dfs_mod* 在shell 中返回ok 之后,htop 在很长一段时间内显示了大量Erlang 活动,所以我不认为主动消息传递会减慢速度。

如何让我的并行 dfs_mod 比其顺序对应的运行得更快?

编辑:当我运行并行 *dfs_mod* 时,pman 根本没有显示任何进程,尽管 htop 显示所有 4 个 CPU 线程都忙。

【问题讨论】:

  • 减速比我预期的要严重,但是对于一个很小的 ​​5 顶点图,你真的不能指望并行算法来提高性能。

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


【解决方案1】:

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

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

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

  • 复制数据、上下文切换和诸如此类的开销使您在并行性方面获得的收益相形见绌。前者尤其适用于大型数据集,您将其分解为子数据集,然后重新组合成一个大数据集。后者尤其适用于高度顺序的代码,如进程环基准测试所示。

如果我想对此进行优化,我会尽量减少消息传递和数据复制。

如果我是这个工作的人,我会保留顺序版本。它按照它所说的去做,当它是一个更大系统的一部分时,一旦你的进程多于核心,并行性将来自对排序函数的许多调用,而不是排序函数的分支。从长远来看,如果是服务器或服务的一部分,使用 N 次顺序版本应该不会比并行版本产生更多的负面影响,后者最终会创建许多更多的进程来执行相同的任务,并且有更多的系统过载风险.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多