Graphchi介绍及BFS实现

随着图规模的逐渐增大,普通PC已无法将整个图一次完全载入到内存中,如何在数据图只能部分载入内存的情况下也能使用数据挖掘算法(如pagerank、wcc等)进行分析?随着Graphchi的出现,这个问题得到了较好解决。Graphchi是由CMU(卡内基梅隆大学)博士Aapo Kyrola开发的一套基于磁盘的图处理系统,该系统声称能有效处理边数目达数十亿规模的数据图。我们都知道计算机中各个层次的数据访问时间:CPU访问寄存器、访问内存、访问磁盘依次存在几个数量级的差异,而数据I/O时间往往成为系统的性能瓶颈,Graphchi使用并行滑动窗口(parallel sliding window)来处理存储在磁盘上的图数据,减少了磁盘的随机读写,从而减少了处理过程中的磁盘访问时间。首先Graphchi将图中所有顶点按id由小到大划分成几个区间(interval),然后每个区间对应一个shard用来存放该区间内所有顶点的入边(边先按目的点排序再按源点排序),这样就完成了图的划分。图算法执行时将各个interval及对应shard中的入边(memory shard)和聚集其他shard中的出边(sliding shard)载入内存进行处理。原理如下图所示:

PSW

Graphchi介绍及BFS实现_第1张图片
这样就使得对存放在磁盘中的边的访问绝大部分是顺序进行的,减少了随机访问可能导致的系统性能下降。
(Graphchi论文 OSDI’12)
Graphchi采用了以顶点为中心(vertex centric)的编程模型,相邻顶点之间通过边来传递消息,用户在实现图挖掘相关算法时只用考虑单个顶点如何处理,具体细节如顶点并行等交由graphchi engine解决。系统对外提供了一个名为GraphchiProgram的抽象结构体(相当于算法的模板),该结构体内包含了以下几个虚函数:

virtual void before_iteration(int iteration, graphchi_context &gcontext)

virtual void after_iteration(int iteration, graphchi_context &gcontext)

virtual bool repeat_updates(graphchi_context &gcontext)

virtual void before_exec_interval(vid_t window_st, vid_t window_en, graphchi_context &gcontext)

virtual void after_exec_interval(vid_t window_st, vid_t window_en, graphchi_context &gcontext)

virtual void update(vertex_t &v, graphchi_context &gcontext)=0

其中重要的是纯虚函数update,用户在继承GraphchiProgram时必须实现update函数(系统对每个顶点都调用update函数),在函数体中写入自己的顶点处理逻辑。

Graphchi引擎在每次迭代中,对每个interval首先调用before_iteration,然后调用before_exec_interval(可以不实现),接下来就是update,然后调用after_exec_interval,将整个图中的所有顶点(即所有interval)遍历一遍后调用after_iteration(我们可以结合before_iteration来判断算法是否已近结束)

下面我们用Graphchi的编程模型来实现BFS算法,通过运行BFS,将输入的图中的所有顶点根据距离源点的距离标记不同的level,Graphchi最后统计并输出拥有最多顶点数的前N个level及对应的顶点数。首先进入项目的example_apps目录下,建一个名为bfs.cpp的文件,复制application_template.cpp里面定义的几个方法,将顶点和边的数据类型定义为unsigned int(顶点值表示与BFS源点的距离或者BFS的层数),并增加一个bool类型的converge变量。将顶点id为1的点设为BFS算法开始的点(根据输入的图可做相应的修改)。接下来实现update函数里面的逻辑

  • 在函数before_iteration中,将自定义的bool类型的converge置为true
  • 在第一次迭代中将选定的起始点的level值置为0,将其他的顶点的level置为无穷大,每个顶点将初始化后的level值写到对应的出边上
  • 在接下来的迭代中,每个顶点读对应的所有入边上的level值,找出最小的层数值并加1,然后与自己的层数进行比较,若比自己的小则更新顶点level值并把更新的值写入到所有出边中,并将converge置为false(表示还有顶点的状态在改变,算法还没有收敛,需要继续迭代)。
  • 在after_iteration函数中判断本次迭代中所有顶点是否已经全部收敛(根据converge的值判断),若收敛则算法结束。在main函数中将迭代次数设为1000(最大允许迭代次数),声明一个bfs和engine的对象,调用engine的run函数并将bfs对象作为参数传入。修改项目主目录下的Makefile文件,将example_apps/bfs加入apps,保存后,make一下,最后在主目录下运行命令:
 bin/example_apps/bfs --file=/home/data/web-Google.txt --membudget_mb=200 --source=1

其中参数 file 指定输入图的路径(可以绝对或者相对)membudget_mb指定分配给Graphchi的内存大小,source指定BFS算法的起始点(还可以通过参数指定算法运行和I/O的各自线程数目)
Graphchi下bfs.cpp下载
测试数据可以在这里下载
算法运行结束即可看到BFS顶点数目及层数的统计结果:-D

你可能感兴趣的:(Algorithm,linux)