GraphChi 是由卡耐基梅隆大学设计, 可以在单机上进行高效大规模计算的框架, 区别于将图的信息全部存储在内存当中, GraphChi 利用单机计算机海量的硬盘进行存储, 由于硬盘与内存的访问速度差距很大, 为了弥补使用硬盘存储带来的缺陷, 他们设计出并行滑动窗口技术, 用来减少硬盘的随机读写。
将整个图谱划按照点的顺序分成不同的分片,每个分片能够完全在内存中进行
处理。 如下图所示:
对每个分片中的入边按照源点进行排序, 基于这种原则出边分布在所有的分片
上, 而且占据一段连续的空间。 这样对于数据的更新, 首先在内存中进行计算和
存储, 随后连续的写入其他分片中, 这样能够很好解决随机读写带来的高延迟
问题, 如下图所示。
GraphChi 延续了 GraphLab 中采用的以点为中心的编程模型, 针对图谱中的节点和边都携带有用户自定义的数据。 在每一次迭代中, 同一个分片中被标记过的点并行调用update 函数进行数据更新: 获取该点入边携带的信息,出边携带的信息, 该点携带的信息,通过用户自定义的计算逻辑,对边和点携带的信息进行更新。 如下图所示:
用精确求解图的直径,需要通过从每一个点出发,进行一次 BFS 找到离该点最远的距离,最终所得值当中的最大值即为图的直径。 在图很大的情况下,这种方法带来的时间开销是无法容忍的,因此容易想到的一个方法是: 选取其中 K 个节点, 分别求出对应的最远距离,然后取最大值近似作为图的直径。
这种想法很直观: 从 K 个节点分别做 K 次 BFS, 但是这种做法的时间开销比较大,从 K个节点出发, 有一部分点集到这 K 个节点的距离是一样的, 从另一个角度来讲, 从 K 个节点出发形成的 BFS 搜索树之间可能存在相同的路径,对于这些相同的路径其实只需一次遍历就行。因此论文中提出的方法,其实是对每个节点进行状态标记, 用 k 位的二进制数标记哪些源点出发的路径已经访问过该点, 每次迭代过程,其实是对每个节点进行状态间的转移,对于那些从源点已经经过该点的路径,不在对该点进行更新。
update 函数
访问该点所有的入边, 将入边的所有权值取或作为 new_state, 该点以前的权值作为 old_state, 判断 old_state 是否完全包含 new_state,即判断是否有从新的源点出发的路径来访问该节点,如果是,则更新该节点的状态。
如果该节点的状态被更新, 将该节点所有的出边的权值都设为该节点的状态,并且将该边所指向的节点加入下一次迭代的队列。
入边权值与出边权值共用带来的问题
通过上述方式实现后,发现求出的图的直径与实际值差距较大,通过分析源代码之后,发现: 对于同一条边对应的入边和出边, 它们所对应的权值指向通一块存储地址,也就是说:入边和出边的权值始终都是一样的,这在异步执行的情况下就引入了一个问题:在同一次迭代中, 对于同一条边,如果出现某个节点把出边的权值修改, 而有节点需要使用入边的权值, 此时使用的权值不是上一次迭代传来的权值,而是新的权值。
为了解决这个问题, 对于边的权值, 引入了两个权值的概念: “当前权值” 和“下一轮权值”, 在一次迭代中, 对入边的访问,使用“当前权值”,而对于出边的修改, 使用“下一轮权值”。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/sunliymonkey/article/details/47983349