【发布时间】: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()会减慢速度? - 我尝试在不收集
done和wait消息的情况下运行*dfs_mod*,但是在*dfs_mod* 在shell 中返回ok之后,htop在很长一段时间内显示了大量Erlang 活动,所以我不认为主动消息传递会减慢速度。
如何让我的并行 dfs_mod 比其顺序对应的运行得更快?
编辑:当我运行并行 *dfs_mod* 时,pman 根本没有显示任何进程,尽管 htop 显示所有 4 个 CPU 线程都忙。
【问题讨论】:
-
减速比我预期的要严重,但是对于一个很小的 5 顶点图,你真的不能指望并行算法来提高性能。
标签: erlang parallel-processing depth-first-search