Pangolin: An Efficient and Flexible Graph Mining System on CPU and GPU
Pangolin: 一个高效灵活的基于 CPU 和 GPU 的图挖掘系统 [Paper] [Slides] [Code]
VLDB’20
摘要
一个针对共享内存CPU和GPU的内存图模式挖掘(Graph Pattern Mining, GPM)框架.
第一个为GPU处理提供高度抽象的GPM系统.
1. 介绍
由于生成大量中间数据等原因, GPM 算法难以实现并行.
现有 GPM 框架的低性能原因:
- 对特定于应用的优化支持有限. 关注于通用性和高度抽象, 从而限制了对算法的优化
- 并行操作和数据结构的实现低效
文章贡献:
- 调查了在先进 GPM 系统和手动优化方案之间的性能差距
- 提出了高性能内存 GPM 系统, 提供特定于应用程序的优化, 第一个为 GPU 处理提供高度抽象.
- 提出了枚举搜索空间剪枝和消除同构的新技术
- 提出了利用底层硬件的高效并行实现
- 评估性能优越
2. 背景
2.1 图模式挖掘
- GPM 问题:
- 显示模式问题: 只寻找嵌入(如三角形计数)
- 隐式模式问题: 根据属性同时寻找模式和嵌入(如频繁子图挖掘)
- 两种嵌入类型:
- vertex-induced embedding(结点诱导嵌入): 通过结点连接的结点寻找子图
- edge-induced embedding(边诱导嵌入): 包含所需边的结点构成子图
- Def: 模式在输入图中出现的频次称为 support(支持度).
2.2 手工优化的 GPM 应用程序
2.3 现有 GPM 框架
现有 GPM 系统以分布式内存或核外平台为目标, 都不是以多核CPU或GPU上的内存GPM为目标的.
不太注意减少CPU/GPU内线程之间的同步开销或减少内存消耗开销.
- API 的局限: 现有的GPM框架中缺失大多数特定于应用程序的优化(如修剪枚举搜索空间和避免同构测试), 因为它们侧重于提供高级抽象.
- 嵌入的数据结构: AoS(array of structures, 结构数组)
- 数据结构具体化(Materialization): 总是在内存或磁盘具体化.
- 动态内存分配: 现有系统使用标准库(
std
)的 map 和 set, 内部使用全局锁动态分配内存, 限制性能和扩展性.
3. Pangolin 框架的设计
- Pangolin API: 用户编写 GPM 应用的 API
- Execution Engine: 遵循以嵌入为中心的模型(embedding-centric model)
- Helper Routines: 被封装并提供给用户的重要的通用操作, 针对CPU和GPU进行了优化
- Embedding List Data Structure: 利用不同硬件特征对不同架构进行了优化
3.1 执行模型
Extend-Reduce-Filter model
- Extend 阶段: 获取输入工作列表中的每个嵌入, 并使用顶点(顶点诱导)或边(边诱导)对其进行扩展, 以将生成的嵌入构成下一级工作列表.
- Reduce 阶段: 提取基于模式的静态信息. 对嵌入分类计算支持度, 并构成模式支持度映射.
- Filter 阶段: 移除用户不感兴趣的嵌入
Reduce 和 Filter 阶段可选. 若都使用则在算法 1 的 4~6 间也会执行; 若只使用 Reduce, 则只需在最后一次迭代执行.
3.2 编程接口
-
ToExtend()
和ToAdd()
供用户主动修剪嵌入候选对象.ToAdd()
可用于消除自同构. -
GetParttern()
: 根据嵌入获取模式, 否则要使用通过自同构测试. -
GetSupport()
和Aggregate()
分别指定对嵌入的支持度和对支持度的 Reduce 操作. -
ToDiscard()
: 用于移除不感兴趣的模式.
现有 GPM 系统的ToExtend()
和GetParttern()
不对外开放, 但以嵌入为中心(embedding-centric)的框架可以进行扩展, 但基于关系模型(relational model)的系统难以实现, 原因在于表连接操作(table join operation)不够灵活.
Pangolin 使用基于 BFS 的嵌入搜索.
3.3 Pangolin 中的应用程序
4 Pangolin 中支持特定于应用程序的优化
4.1 剪枝枚举搜索空间
- 有向无环图(DAG): 将无向图转为有向无环图(DAG). 每条边指向度数更大的结点, 度数相同时指向 ID 更大的结点.
- Eager Pruning(热切剪枝): 通过
toExtend()
函数提供给用户指定剪枝方案的接口. 如 CF 中的toExtend()
函数只扩展嵌入的最后一个结点, 来避免重复.
4.2 省略同构测试
Exploiting Memoization(利用记忆): 通过在同构测试后的每个嵌入中维护模式ID(哈希值), 并在模式ID和模式支持度之间建立映射, 可以避免嵌入模式的重新计算. [存疑]
定制模式分类: 通过 getPattern()
函数提供给用户自定义模式分类的接口. 如 3-MC 中可以通过边数来直接确定模式类型.
5 在 CPU 和 GPU 上的实现
CPU 实现使用 Galois 库构建, GPU 实现使用 LonestarGPU 基础架构构建.
架构优化: (1)利用局部性和充分利用内存带宽; (2)减少内存消耗; (3)减少动态内存分配的开销; (4)最小化同步和其它开销.
5.1 嵌入的数据结构
使用 SoA(structure of arrays, 数组的结构体) 而非 AoS(array of structures, 结构体的数组) 在内存中存储嵌入, 利于 GPU 上并行处理.
每个嵌入都可以通过从最后一个级别列表中回溯来重建.
边诱导的情况需要添加一列 his
, 即每个条目是 (idx, his, vid)
的三元组. his
指示当前边的源点, vid
指示当前边的终点.
这种数据布局可以通过更多的数据重用来改善时间局部性. 如 与 中的两个顶点相连接, 因此
5.2 避免数据结构具体化
- Loop Fusion(循环融合): 在嵌入添加到工作列表之前使用
toAdd()
函数丢弃(不符合的)嵌入, 以避免候选嵌入的具体化(Materialization, 理解为根据当前每一层的候选结点集生成当前的嵌入子图, 子图的每个结点与模式图结点相对应). - Blocking Schedule(分块编排): 边分块(edge-blocking)技术. 划分初始嵌入列表为更小的块(chunk). 由比块大小更少的线程来依次处理每个块中所有级别的嵌入, 在处理完当前块的所有级别之后线程移动到下一个块继续处理.
Filter 阶段需要每个等级的严格同步, 使用其的应用程序不能使用边分块技术(边分块技术在处理一个块中的嵌入时可能没有考虑每个级别嵌入的同步).
边分块技术专门用于限制内存使用, 防止内存耗尽.
5.3 动态内存分配
- 检查-执行(Inspection-Execution): 不同线程写入共享嵌入列表时可能产生冲突.
生成过程: (1)只计算新生成的嵌入数量 (2)计算每个新嵌入的起始索引, 并根据数量分配内存 (3)写入嵌入列表新的嵌入
缺点: 需要迭代嵌入两次
优点: 改进存储访问模式, 以更好利用内存带宽; 扩展性 - 可扩展分配器(Scalable Allocators): 使用 Galois 内存分配器提供的 std 标准库的数据结构变体. Galois 分配器使用巨大页面的每线程内存池以避免加锁.
5.4 其它优化
推广了 TC 提出的二分搜索的方法, 以实现结点连通性检查. 提供了高效的 CPU 和 GPU 实现(isConnected()
).
6. 评估
性能:
扩展性: Figure 12
GPU 性能: Figure 14
内存消耗: Figure 15
各种优化的性能提升: Figure 16, Figure 17, Figure 18
笔者总结
本文的核心在于将多个图挖掘问题统一化, 提出了一个统一的图挖掘模型: Extend-Reduce-Filter 模型, 并向用户提供了多个 API 接口.
其中的多个优化也是围绕着这些 API 进行的.
Pangolin 系统属于通用图挖掘系统, 采用以嵌入为中心(Embedding-Centric)的图挖掘模式, 支持结点诱导(vertex-induced)和边诱导(edge-induced)的子图扩展方式.