解析内存中的高性能图结构

news2024/9/22 9:42:34

在进行各种图处理、图计算、图查询的时候,内存或是硬盘中如何存储图结构是一个影响性能的关键因素。本文主要分析了几种常见的内存图结构,及其时间、空间复杂度,希望对你有所启发。

通常来说,对于图结构的几种常见的基础操作:

  1. 插入一个点
  2. 插入一个边
  3. 删除一个边
  4. 删除一个点的全部邻边
  5. 找到一个点的全部邻边
  6. 找到一个点的另一个邻点
  7. 全图扫描
  8. 获取一个点的入度或者出度

这些图相关的操作,除了要关心时间复杂度之外,需要考虑空间占用的问题。

对于大多数实时读写型的系统,增删改查的性能问题会比较重要,它们比较关注上面 1-6 的操作;对于部分密集计算的系统,对批量读取的性能会比较重视,侧重上面 5-8 的操作。

不过遗憾的是,无论是常规的图查询,还是进阶的图计算,根据 RUM 猜想[1],读快、写快、又省空间这样”既要又要也要”的好事是不存在的。

下面,我们介绍几个数据结构并给出少量的定量分析。

我们先从三个典型的方案(邻接矩阵、压缩稀疏矩阵和邻接表)说起,再介绍几种近几年的研究的变种结构 PCSR、VCSR、CSR++。

邻接矩阵 Adjacency Matrix

用矩阵来表示图结构是大学里数据结构课程中都学过的知识,也是一种非常直观的办法。

点 i 与点 j 之间有一条边,等价对应于矩阵上 M i j M_{ij} Mij 为 1。当然,这里的点 ID 是需要连续排列无空洞。

用矩阵来表示图结构有明显的好处,可以复用大量线性代数的研究成果:比如图上模式匹配类的问题等价于矩阵的相乘。

下面是一个模式匹配问题,它的大体意思是在全图上寻找这样一种图结构:

Match (src)-[friend * 2..4]->(fof)
WHERE src.age > 30
RETURN fof

对于这样一个问题可以直接对应右边的矩阵操作:

比如,2 跳遍历等价于矩阵 F 自乘;2 - 4 跳遍历分别等价于 F2、F3、F^4;取属性等价于乘以一个过滤矩阵。

更进一步,由于矩阵操作是天生可以分块并行加速,这在性能上有极大的优势。

下面,我们对邻接矩阵操作进行一个简单的定量分析

操作时间复杂度备注
插入一个点O(n^2)对于矩阵来说,增加一个点意味着整个矩阵的维度增加,通常需要另外开辟一块空间
插入一个边O(1)增加一个边只是将对应的位置置 1
删除一个边O(1)置0
删除一个点的全部邻边O(n)对于某个点所有出边的删除对应某一行的置0。入边对应某一列,可以批量操作
找到一个点的全部邻边O(n)
找到一个点的给定邻点O(1)
全图扫描O(n^2)

其中,n=|V|,m=|E|。

优化上,批量操作(CPU Cache/SSD block)可以线性增加性能,例如 O(n) 可以降低到 O(n/B),但不影响定量分析。

由于绝大多数图结构是极其稀疏的,因此简单用邻接矩阵来表示图结构,其内存会有夸张的浪费。更为严重的是,当有多种边类型时,每种边类型各需要一个邻接矩阵。这使得裸用矩阵在实际情况中只能处理很小数据量的场景。当然,对于现代服务器动辄几百 G 的内存,如果只有几亿点边的数据量,像是 twitter2010,这并不会是很严重的问题。但大多数情况下,条件允许的话,大家还是希望找到一些更加经济的结构。

压缩稀疏矩阵 CSR/CSC

压缩稀疏矩阵是一种非常流行和紧凑的图结构表示方式,大多数图计算系统都采用 CSR。

这里简单介绍一下 CSR 的结构:

对于点 IDx,取邻居 ID 就是 F[N[x]] 到 F[N[x+1]-1]。

例如,查找点 ID2 的邻居,对应为 F[N[2]] 到 F[N[2+1]-1],对应到上图也即 1 6 8。查找点 ID7 的邻居,对应为 F[N[7]] 到 F[N[7+1]-1],也就是 2 和 4。

另外,CSR 记录的仅是出边的信息,如果要考虑入边就使用 CSC,其原理是类似的。

操作时间复杂度备注
插入一个点O(1)在点矢量尾部增加
插入一个边 <u,v>O(m+n)边矢量,从 u 对应的邻居开始,都向后移动一位;点矢量,从 u 对应位置开始每个值加 1
删除一个边O(m+n)插入边的逆过程
删除一个点 v 的全部邻边O(m+n)边矢量移动 deg(v) 位,点矢量 u 对应位置置 0
找到一个点的全部邻边O(deg(v))
找到一个点的给定邻点O(log(deg(v)))deg(v) 内的排序查找
全图扫描O(m+n)

CSR 的空间占用是 O(|V|+|E|),在空间节省和顺序查找上是极其高效的。但在大量插入时,压缩稀疏矩阵和邻接矩阵一样,需要重新开辟空间,效率很低。所以,它适合于计算密集场景但不适合增改频繁的场景。

CSR 还有一个显著的优点是可以快速获取每个点的出入度,只要计算 N[x+1]-N[x],这在判断一些点是否为超级节点时很方便。如果不是稀疏矩阵的话,通常会用另外一个单独的结构来记录出入度。

此外,CSR 不容易实现并发修改,其每次插入都需要对两个矢量进行位移,这并不高效。

这里推荐两个相关的开源项目,可以进一步了解下 CSR 的使用:

  • https://github.com/DrTimothyAldenDavis/GraphBLAS
  • https://github.com/GraphBLAS/LAGraph

邻接链表 Adjacency List

和基于矩阵的方式不同,邻接链表 AL 空间上有优势,但对于边的读写上会略微慢一点(指针在内存中不能连续移动)。AL 的做法是把邻边(出边)用 list 或者有序 list 的方式串连起来。由此延伸的一个变种是邻边从 list 改为 array。

操作时间复杂度备注
插入一个点O(1)尾插入
插入一个边 <u,v>O(log(deg(u)))有序 list
删除一个边O(log(deg(u)))
删除一个点 v 的全部邻边O(1)
找到一个点的全部邻边O(deg(u))
找到一个点的给定邻点O(log(deg(u)))
全图扫描O(n+m)

AL 相比 CSR 通常不能直接获得点的出入度,可以通过可以单独维护一个字段实现该功能。

此外,邻接表并不需要 ID 连续排布,对于频繁增删点的场景特别友好。AL 对于并发修改的支持也更友好,天然在点级别有并行度。当然,对于超级节点,直接用 list 的方式,还是会有些性能的问题要考虑;优化上可能会进一步改造成 Blocked list 的方式,可以带来更好的数据局部性和细颗粒度。

由于 ID 不用严格连续排布,AL 的一个常见变种就是 Tree。

Tree

在这种结构中,一个点和其所有的邻边被建模成了 key-value,key 是点 ID,value 是所有邻边的编码。Key 通过 Tree 的方式组织在一起。这里的树可以是 B-Tree 等各变种 Tree。虽然本文没有讨论图属性,但 value 中也是可以存放 value。

操作时间复杂度备注
插入一个点O(log(n))
插入一个边 <u,v>O(deg(u) log(n))
删除一个边O(deg(u) log(n))
删除一个点 v 的全部邻边O(log(n))
找到一个点的全部邻边O(deg(u) log(n))
找到一个点的给定邻点O(log(n deg(u))
全图扫描O(n+m)

为了控制访问颗粒度,每个叶子通常会被限定为固定的大小(页)。这就是在数据库类系统里面最常见的办法 B-tree。为了增改方便,也可以把每页的 in-place update 改成 Copy-On-Write 的方式;一个典型的代表就是 LLAMA[2],但这种多版本的读取通常会需要更多的空间,并且当有大量累积修改时,需要定期的多版本合并以降低跨快照读。某种程度上,它和 LSM-Tree 的思路有些接近。

基于 CSR 的变种 PCSR 和 VCSR

由于 CSR 在空间和读性能上有很大的优势,但在插入时的耗时和空间上都很弱,因此本节几个变种的主要目的都是为了改善其弱项,大体思路都是分块和 buffer。

在 CSR 的边矢量进行增删时可以注意到,主要耗时是在对于矢量的元素位移上。因此,一个直观的思路是预留一些插入空白位,在删除时也不立刻回收这些空白。

而分块思想,是指将一些局部数据放在同一个分块内,例如 Tree 中每个 page 就是一种分块的方式。与此对应的是,buffer 空白之间的连续区域。

PCSR

PCSR [3]的基本思想是:对于点矢量,其元素从一个值改为对应边矢量中对应邻边位置的 <起点,终点>。而对于边矢量,在这些分块所对应边界放置哨兵 sentinel,上图中的 S,上图的 “-“ 对应预留插入位置留空。

事实上,原文中,对于边矢量,其本质是实现为一个 B-Tree,本文先简化成一个 Array。

操作时间复杂度备注
插入一个点O(1)直接在点矢量和边矢量的尾插入
插入一个边 <u,v>O(log(deg(u))边矢量对应分片的二分+空位查找
删除一个边O(log(deg(u))
删除一个点v的全部邻边O(1)边矢量对应分片置空,点矢量对应 ID 位置置空
找到一个点的全部邻边O(deg(u))
找到一个点的给定邻点O(log(deg(u))
全图扫描O(n+m)

可以看到,PCSR 的预留位置多少都是需要重平衡,不能过多也不能不足。特别是大批量增删时,对预留位置的处理会是一个较重的操作。

此外,如果把 PMA 的 B-Tree 以及需要 rebalance 的本质考虑进去,其和前述 Tree 方式的区别并不是很大。

VCSR

VCSR[4]主要是对 PCSR 的一个改进,其朴素思想是 PCSR 的留空是均匀的,而大多数图结构的出入度,是存在 20-80 这样的幂率特征,而 PCSR 的一个主要痛点是频繁的 rebalance。VCSR 的做法是为每个分块预留空间正比于其分块内的点的数量,即:边矢量中,一个分块内,如果点的数量多,就多预留一些空位。在直觉上,点数量多时,其分块对应的边插入会更多一些,这样可以减少 rebalance 的频率。

此外,VCSR 还有些版本号之类的优化。

CSR++

事实上,CSR++[5]在设计上其实更接近一种 AL/Tree 的变种,而不是 CSR。它主要有三个方面的优化:

第一,对于 Vertex Array 再分段,将一个大的 Array 拆成多段,这样可以有更细的读写颗粒度。通过 段 ID + 点 ID 来定位每个点和其邻边。
第二,Vertex Array 中每个元素,除了记录点 ID 之外,对于邻边数量很少的点,直接把邻居 ID 也对齐地塞 inline 进去

对于大多数的点,其邻边就不需要单独的 Edge Array 来存储了。

可以看到这种方式在图比较稀疏的时候,对于 CPU Cache 扫描是很友好的。

第三,对于每个点的邻边,采用copy-on-write、标记删除等常见的优化办法,构建成类似 std::vector 结构。

小结

最后,由于在图查询、图存储和图计算不同场景下,对于图结构的读写扫描和生命周期都有些不同的要求,不同的数据结构也有不同的优劣。

当然,本文只是讨论了图结构可以放在内存中的情况。当不得不把部分数据放在硬盘上时,问题就完全不同了。当然本文也没有讨论不同 CPU 对于不同距离内存的性能差异 NUMA,或者跨进程通信带来的影响。

延伸阅读

最后,我们来了解下在图计算/图算法上的图操作。

图算法中的图操作

在图计算中,存在多种图结构算法,可能会涉及多种基础操作。

中心性 Centrality Algorithms,一种衡量一个节点在整个网络图中所在中心程度的算法,包括:度中心性、接近中心性、介数中心性等,会涉及“找到一个点的全部邻边”、“找到一个点的另一个邻点”、“全图扫描”的操作组合。

  • 度中心性通过节点的度数,即关联的边数来刻画节点的受欢迎程度,这将会要求找到一个点的所有邻边。
  • 接近中心性,通过计算每个节点到全图其他所有节点的路径和来刻画节点与其他所有节点的关系密切程度,这将会要求进行全图扫描,查找点和图中所有点的路径信息。
  • 介数中心性,则用于衡量一个顶点出现在其他任意两个顶点对之间最短路径上的次数,从而来刻画节点的重要性,这将会要求进行全图扫描,找到一个点和它的邻点及路径信息

PageRank,又称网页排名、谷歌左侧排名,是一种由搜索引擎根据网页之间相互的超链接计算的技术作为网页排名的要素之一的排名方法。它以 Google 公司创办人拉里·佩奇 Larry Page 之姓来命名。Google 用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。一般来说,PageRank 的值越高,表示其被很多其他网页链接到,说明它的重要性很高;而如果一个 PageRank 很高的网页链接到其他网页,被链接的网页的 PageRank值也会随之提高。PageRank 的计算过程如下:

假设由 4 个网页组成的集合:A、B、C、D,每个页面的初始 PageRank 值相同,为了满足概率值为 0-1 之间,假设这个值为 0.25。

如果所有页面都只链接至 A,如下图,则 A 点的值将是 B、C、D 的 PageRank 值的和。

倘若是下图这种图结构:

B 链接到 C,D 链接到 B 和 C,A 点的值计算的方式如下:

这里的 2、1、3,分别是 B 点对外链接的 2 条边,C 点对外链接的 1 条边,D 点对外链接的 3 条边。

换句话说,算法将根据每个页面连出总数平分该页面的 PR 值,并将其加到其所指向的页面:

最后,所有这些 PR 值被换算成百分比形式再乘上一个修正系数。

由于“没有向外链接的网页”传递出去的 PR 值会是0,这时候如果递归的话,会导致指向它的页面的 PR 值的计算结果同样为零,所以 PageRank 会赋给每个页面一个最小值。

因此,一个页面的 PR 值直接取决于指向它的的页面。如果在最初给每个网页一个随机且非零的 PR 值,经过重复计算,这些页面的 PR 值会趋向于某个定值,也就是处于收敛的状态,即最终结果。

简单来说,大多数迭代图计算模型都是基于“找到一个点的全部邻边”、“找到一个点的另一个邻点”操作。

AIGC 小课堂

在 AIGC 小课堂部分,我们会用 NebulaGraph 接入的 aibot 来讲解下文中的部分概念。你如果对 chatgpt 或者是 aibot 有兴趣,可以来 https://discuss.nebula-graph.com.cn/ 向 aibot 提出你的要求。

参考文献

  1. Athanassoulis, M., Kester, M. S., Maas, L. M., et al. (2016). Designing Access Methods: The RUM Conjecture. In EDBT (pp. 461-466).
  2. Macko, P., Marathe, V. J., Margo, D. W., et al. (2015). Llama: Efficient Graph Analytics Using Large Multiversioned Arrays. In 2015 IEEE 31st International Conference on Data Engineering (pp. 363-374). IEEE.
  3. Wheatman, B., & Xu, H. (2018). Packed Compressed Sparse Row: A Dynamic Graph Representation. In 2018 IEEE High Performance Extreme Computing Conference (HPEC) (pp. 1-7). IEEE.
  4. Islam, A. A. R., Dai, D., & Cheng, D. (2022). VCSR: Mutable CSR Graph Format Using Vertex-Centric Packed Memory Array. In 2022 22nd IEEE International Symposium on Cluster, Cloud and Internet Computing (CCGrid) (pp. 71-80). IEEE.
  5. Firmli, S., Trigonakis, V., Lozi, J. P., et al. (2020). CSR++: A Fast, Scalable, Update-Friendly Graph Data Structure. In 24th International Conference on Principles of Distributed Systems (OPODIS’20).

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/506638.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过SSH隧道安全消费Kafka数据

一.背景 由于我们有个业务在阿里云部署了Kafka&#xff0c;但是想直接在本地IDC机房服务器直接通过公网消费Kafka进行业务处理。这个本来也不是什么难事&#xff0c;阿里云把9092默认端口打开运行访问即可&#xff0c;也不不值得再写这篇博客了。 这个事情让人特别关注的一个主…

【记录】ChatGPT|图片预览魔法咒语魔改,使用 ChatGPT 返回大量可以跳转的链接

很早的时候&#xff0c;我已经留意到 ChatGPT 会以返回图片的 markdown 格式来显示图片&#xff0c;很可能拥有一定的图片上传功能&#xff0c;但是它往往会显示得有些问题。一些代码图片之类的或者风景图什么的都不是很会。 但其实 ChatGPT 是可以直接返回图片类型的回复的&a…

C++初始化列表

1.初始化列表概述 初始化列表&#xff1a;以一个冒号开始&#xff0c;接着是一个以逗号分隔的数据成员列表&#xff0c;每个"成员变量"后面跟一个放在括号中的初始值或表达式。 2.为什么使用初始化列表 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c…

个人电脑操作系统

UEFI&#xff08;Unified Extensible Firmware Interface&#xff0c;全称统一的可扩展固件接口&#xff09;是一种个人电脑系统规格&#xff0c;用来定义操作系统与系统固件之间的软件界面&#xff0c;作为BIOS的替代方案。其前身是Intel在1998年开始开发的Intel Boot Initiat…

【static_cast、reinterpret_cast、const_cast、dynamic_cast】C++类型转换

C类型转换 引入C语言中的类型转换 C的强制类型转换static_castreinterpret_catconst_castdynamic_cast向下转型的安全问题 explicit4种类型转换的应用场景 RTTI 引入 C语言中的类型转换 C语言和C都是强类型语言&#xff0c;如果赋值运算符左右两侧变量的类型不同&#xff0c;…

ChatGPT一键私有部署,全网可用,让访问、问答不再受限,且安全稳定!

前言 ChatGPT由于在访问上有一些限制&#xff0c;使用并不便利。目前国内可以直接访问的大部分是调用API返回结果&#xff0c;我们去使用时总会有次数限制&#xff0c;而且它们可能随便崩掉。 其实&#xff0c;目前我们访问过的大部分国内的网页包括UI&#xff0c;其实是套用了…

过滤器对前端请求参数进行解码URLDecoder,接口接收参数类型为map,解码无效问题

文章目录 一、前言二、设计思路三、代码实现四、启动测试五、过滤器解码无效六、源码跟踪七、解决方案八、再次重启测试九、总结 一、前言 最近做的一个公司项目&#xff0c;因为客户需要对特殊字符做搜索&#xff0c;但是前端的请求参数无法传递到后端&#xff0c;所以前端对…

VS2017中Qt项目数据库连接——包含报错比如QMYSQL driver not loaded(细心看到最后,一定能解决你想解决的问题)

我把爆的错误QMYSQL driver not loaded写在文章末尾了&#xff0c;大家看一看&#xff01;前面是配置数据库 一、测试 VS2017 中 Qt 项目数据库连接 打开 VS2017&#xff0c;文件——新建项目&#xff0c;右侧输入框输入 Qt 确定后点击下一步&#xff0c;勾选模块 基类也是默…

RSU路测单元,你知道多少?

一、什么是RSU路测单元&#xff1f; RSU路测单元是实现智慧的路、车路协同的关键设备&#xff0c;设置在路侧&#xff0c;与附近过往车辆进行双向通信、交互数据&#xff0c;是智能交通系统中的一种重要设备。RSU可以连接路面原有电子设备&#xff0c;比如信号灯和摄像头&…

一文解决Xshell无法连接vmware上的centos

问题描述 win10系统上安装VMware workstation16 pro&#xff0c;装好后安装centos虚拟机&#xff0c;在设置network & hostname时选择的NAT模式&#xff0c;即使用自定义的网关和IPv4地址&#xff0c;最后配置完成后centos主机地址信息如下&#xff0c;在虚拟机内部进行pi…

【一览无余】Vue框架下Cesium加载遥感地图使用GeoServer切割TIF大文件对外发布WMS服务进行地图绘制(科普篇2/2)

【一览无余】Vue框架下Cesium加载遥感地图使用GeoServer切割TIF大文件对外发布WMS服务进行地图绘制&#xff08;科普篇2/2&#xff09; 二、Cesium是弄啥嘞2.1 WebGL2.1.1 WebGL是什么2.1.2 WebGL优点 2.2 Cesium是什么2.3 Cesium能干什么2.4 Cesium相关工具有哪些2.5 相关案例…

Echarts 3D散点图

文章目录 以下是一个 html echarts的案例 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>ECharts 3D Scatter Plot Demo</title><!-- 引入 ECharts --><script src"https://cdnjs.cloudflare.com/…

急吗?光急可没用呀!满满干货,两小时速成,别搁那干瞪眼了!

全球产业链加速重构&#xff0c;各种不确定性加大&#xff01;数字经济规模不断提升&#xff0c;为企业转型与发展创造大量机会&#xff01;企业亟须以数字化为工具或手段&#xff0c;再造组织流程和业务流程&#xff0c;以数字化确定性应对外部环境变化的不确定性&#xff0c;…

中检集团:把数智化转型作为“1号工程”

2018年4月&#xff0c;中国检验认证集团&#xff08;以下简称“中检集团”&#xff09;启动信息化建设“1号工程”&#xff0c;5年时间&#xff0c;从“数字中检1.0”升级到“2.0”再到“2.1”“2.2”&#xff0c;中检集团在数智化转型过程中&#xff0c;可谓是打了一场攻坚战。…

智能ai文章伪原创工具-智能ai文章原创处理系统

智能AI文章伪原创工具 您好&#xff0c;智能AI文章伪原创工具是一种通过机器学习和自然语言处理技术&#xff0c;帮助用户生成“看起来像是”原创文章的人工智能工具。该工具的原理是将原始文章分解为各个句子和段落&#xff0c;然后对其中的一些单词、短语或句子进行修改或替…

MySQL高级(进阶)SQL语句

#显示一个字段或者多个字段的所有内容 SELECT “字段” FROM 表名 &#xff1b; #distinct对字段去重查询 &#xff08;最好只对单个字段进行去重&#xff09; SELECT DISTINCT 字段名 FROM 表名 #where 有条件查询 SELECT “字段” FROM 表名 WHERE 条件&#xff08;例如&a…

安卓开发:使用可为null性

Kotlin园地 地址&#xff1a;Kotlin 园地 | Android 开发者 | Android Developershttps://developer.android.google.cn/training/kotlinplayground?hlzh-cn 以下简称 “K园” 先在K园执行以下代码&#xff1a; fun main() {var fa: String "sandra on";prin…

服务攻防-协议漏洞-FTPRDPSSHRsyncProFTPDlibsshOpenssh-Hydra工具使用口令猜解未授权访问

目录 一、导图 二、口令猜解-Hydra-FTP&RDP&SSH 1、协议介绍 2、Hydra工具介绍 3、实例演示 三、配置不当-未授权访问-Rsync 文件备份 1、Rsync介绍 2、漏洞成因——配置不当 3、实例演示 四、协议漏洞-应用软件-FTP&ProFTPD搭建 1、引入 2、ProFTPD介…

IDEA 搭建 Maven模块化项目

目录 1.前言 2. 软硬件环境 3.项目搭建 3.1.创建 SpringBoot 父项目 3.2. 构建子项目centerdao 3.3. 构建子项目centerweb 4. 建立父子 Module 依赖 4.1 删除不必要文件 4.2.修改 Parent 项目 packaging 4.3.修改子项目pom.xml 信息 4.4. 补充说明 5. 项目继承关系…

ios15及以上webview、Safari使用Websocket断连,1006无清晰错误码

文章目录 问题表现&#xff1a;定位疑似原因&#xff1a;解决方式&#xff1a;定位问题思路过程记录&#xff1a;1、对比前端代码运行环境问题2、写纯请求前端代码连接&#xff0c;确认是否接口部署服务问题&#xff1b;3、IOS连接是否有对TSL安全协议版本有要求&#xff08;使…