OlapBase API
此文档主要详细介绍了OlapBase API的使用说明
1. 概述
本手册将介绍使用TuGraph图计算系统需要的简单配置,同时结合代码对TuGraph中几个共同的重要文件和接口进行解释。
2. 配置要求
如果要使用TuGraph图计算编写以及编译自己的应用程序,需要的配置要求为:
- linux操作系统,目前在Ubuntu16.04, Ubuntu18.04, Ubuntu20.04和Centos7, Centos8系统上可成功运行。
- 支持C++17的编译器,要求GCC版本为8.4.0或更新的版本。
3. 原子操作
TuGraph使用了多线程技术进行批处理操作,在这种情况下可能会出现访存冲突现象。为了保证并行计算时修改操作的正确性,TuGraph实现了原子操作。代码部分见lgraph文件夹下的lgraph_atomic.cpp文件。 TuGraph还自定义了4个常用的原子操作。当我们需要在多线程模式下修改点的数据时,我们都应该使用原子操作来确保并行环境下修改操作的正确性。除了这4个原子操作外,用户也可以使用“cas”来构建自己的原子操作函数。
- bool cas(T * ptr, T oldv, T newv):如果ptr指向的值等于oldv,则将ptr指向的值赋为newv并返回true,否则返回false
- bool write_min(T *a, T b):如果b比a指向的值更小,那么将a指向的值赋为b并返回true,否则返回false。
- bool write_max(T *a, T b):如果b比a指向的值更大,那么将a指向的值赋为b并返回true,否则返回false。
- void write_add(T *a, T b):将b的值加到a指向的值上。
- void write_sub(T *a, T b):将a指向的值减去b的值。
4. 点集合类ParallelBitset
在使用TuGraph进行批处理操作时,需要使用点集合来表示需要处理的点。ParallelBitset实现了点集合类,以bit为单位表示点,因此能够节省大量内存。对应的代码见lgraph文件夹下的olap_base.h文件。
4.1 ParallelBitset类成员
- size_t Size():表示Bitmap中的点个数。
- ParallelBitset(size_t size):初始化size和data,data长度为(size >> 6)+1
- void Clear():清空集合
- void Fill():将所有点加入集合
- bool Has(size_t i):检查点i是否在集合中
- bool Add(size_t i):将点i加入集合中
- void Swap(ParallelBitset &other):和另一组ParallelBitset集合交换元素
5. 点数组类ParallelVector
在使用TuGraph进行批处理操作时,需要使用点数组来表示对点的处理结果。ParallelVector实现了点数组类。对应的代码见lgraph文件夹下的olap_base.h文件。
5.1 ParallelVector类成员
- ParallelVector(size_t capacity)构建ParallelVector,capacity为点数组的初始容量大小
- T &operator[](size_t i):下标为i的数据
- T *begin():ParallelVector的起始指针
- T *end():ParallelVector的结束指针。begin和end的用法类似于vector容器的begin和end指针,可以使用这两个指针对数组进行顺序访问
- T &Back():ParallelVector最后一个数据
- T *Data():表示数组本身数据
- void Destroy():清空ParallelVector数组内数据并删除数组
- size_t Size():表示ParallelVector中的数据个数
- size_t Capacity():表示ParallelVector的容量大小
- void Resize(size_t size):更改ParallelVector为size大小,该size应大于等于更改前的大小且小于capacity
- void Clear():清空ParallelVector内数据
- void ReAlloc(size_t capacity):给ParallelVector分配新的容量大小,若数组有数据则将数据迁移至新内存
- void Fill(T elem):为ParallelVector的全部数据赋值为elem
- void Append(const T &elem, bool atomic = true):向ParallelVector结尾添加一个数据
- void Swap(ParallelVector<T> &other):和其他的ParallelVector交换数据
- ParallelVector<T> Copy():复制当前的ParallelVector数据存至Copy数组中
6. 自定义数据结构
6.1 基本数据类型
我们自定义了点和边的数据结构表示,用于在覆盖所有点的同时节省内存空间:
- Empty:内容为空的特殊数据类型。
6.2 组合数据结构
为了便于计算,我们根据计算场景不同,定义了几种点和边数据的数据结构,分别是:
- EdgeUnit<EdgeData>:表示权值类型为EdgeData的边,用于解析输入文件,包含三个成员变量:- size_t src:边的起始点
- size_t dst:边的终点
- EdgeData edge_data:边的权值
 
- AdjUnit<EdgeData>:表示权值类型为EdgeData的边,用于批处理计算过程中,包含两个成员变量:- size_t neighbour:边的邻居点
- EdgeData edge_data:边的权值
 
- AdjList<EdgeData>:权值类型为EdgeData的点的邻接表,常用于表示点的入边和出边集合,包含两个成员变量:- AdjUnit<T> * begin:列表的起始指针
- AdjUnit<T> * end:列表的结束指针。begin和end的用法类似于vector容器的begin和end指针,可以使用这两个指针对邻接表进行循环访问。
 
7. 图类OlapBase
图类OlapBase是TuGraph用于加载图以及进 行图计算操作的主类,常用OlapBase
7.1 基本信息
- size_t NumVertices():获取点数
- size_t NumEdges():获取边数
- size_t OutDegree(size_t vid):点vid的出度
- size_t InDegree(size_t vid):点vid的入度
7.2 点集和边集及其相关操作
- ParallelVector<VertexData> AllocVertexArray<VertexData>():分配一个类型为VertexData的数组,大小为点个数
- void fill_vertex_array<V>(V * array, V value):将数组array中的所有元素赋值为value
- ParallelBitset AllocVertexSubset():分配一个ParallelBitset集合,用于表示所有点的状态是否激活
- AdjList<EdgeData> OutEdges(size_t vid):获取点v的所有出边集合
- AdjList<EdgeData> InEdges(size_t vid):获取点v的所有入边集合
- void Transpose():对有向图进行图反转
- LoadFromArray(char * edge_array, VertexId input_vertices, EdgeId input_edges, EdgeDirectionPolicy edge_direction_policy):从数组中加载图数据,包含四个参数,其含义分别表示:- edge_array:将该数组中的数据读入图,一般情况下该数组包含多条边。
- input_vertices:指定数组读入图的点个数。
- input_edges:指定数组读入图的边的条数。
- edge_direction_policy:指定图为有向或无向,包含三种模式,分别为DUAL_DIRECTION、MAKE_SYMMETRIC以及INPUT_SYMMETRIC。对应的详细介绍见include/lgraph/olap_base.h文件的- enum EdgeDirectionPolicy。
 
7.3 锁机制
TuGraph实现了一对锁机制,来控制程序对于点数据的访存权限。分别是:
- void AcquireVertexLock(size_t vid):对点vid加锁,禁止其它线程对该锁对应的点数据进行访存
- void ReleaseVertexLock(size_t vid):对点vid解锁,所有线程均可访存该锁对应的点数据
- VertexLockGuard GuardVertexLock(size_t vid):在对vid操作时,对点vid加锁,退出作用域时时自动释放锁
7.4 批处理操作
TuGraph提供了两个批处理操作来并行地进行以点为中心的批处理过程。分别是:
/*
    函数名称:ReducedSum ProcessVertexInRange(std::function<ReducedSum(size_t)> work, size_t lower, size_t upper,
                ReducedSum zero = 0,std::function<ReducedSum(ReducedSum, ReducedSum)> reduce =reduce_plus<ReducedSum>)
				
    函数用途:对Graph中节点编号介于lower和upper之间的节点执行work函数。第四个参数表示累加的基数,默认为0;
    第五个参数表示对每个work处理后的节点返回值进行迭代reduce函数操作,默认为累加操作。
    具体实现请参考include/lgraph/olap_base.h中具体代码
    使用示例:统计数组parent数组中有出边的点个数
*/
auto vertex_num = graph.ProcessVertexInRange<size_t>(
    [&](size_t i) {
        if (graph.OutDegree(parent[i]) > 0) {
            return 1;
        }
    },
    0, parent.Size()
);
printf("the number is %lu\n",vertex_num);
其中graph为图类OlapBase的实例化对象
/*
    函数名称:ReducedSum ProcessVertexActive(std::function<ReducedSum(size_t)> work, ParallelBitset &active_vertices,
                ReducedSum zero = 0,std::function<ReducedSum(ReducedSum, ReducedSum)> reduce =reduce_plus<ReducedSum>)
				
    函数用途:对active_vertices中对应为1的节点执行work函数,第三个参数表示累加的基数,默认为0;
    第四个参数表示对每个work处理后的节点返回值进行迭代reduce函数操作,默认为累加操作。
    具体实现请参考/include/lgraph/olap_base.h中具体代码
    
    使用示例:输出Graph中节点1,2,3的所有出度邻居,并统计这三个节点的总出度
*/
auto active_in = graph.AllocVertexSubset();
active_in.Add(1);
active_in.Add(2);
active_in.Add(3);
auto total_outdegree = graph.ProcessVertexActive<size_t>(
    [&](size_t vi) {
        size_t local_outdegree = 0;
        for (auto & edge : graph.OutEdges(vi)) {
            size_t dst = edge.neighbour;
            printf("node %lu has neighbour %lu\n",vi,dst);
            local_outdegree += 1;
        }
        return local_outdegree;
    },
    active_in
);
printf("total outdegree of node1,2,3 is %lu\n",total_outdegree);