切点:又称 “以边为中心的切图”,保证边不被切开,一条边在一台机器上被存储一次,被切的点创建多个副本,副本点所在的机器不清楚关于此点的相关边。如上图所示,中间点被分别保存三个版本,此点会分别出现在三台机器上,在做更新时需要更新三次。切边:又称以 “顶点为中心的切图”,相比于切点,保证点不被切开。边会被保存两次,作为副本点所在机器能清楚感知到此点的相关边。如上图所示信息只进行一次更新。Gemini 采用切边的方式进行存储。定义抽象图为 G (V,E),Gemini 定义了主副本(master)与镜像副本(mirror),计算时是以 master 为中心进行计算。如下图所示,集群每台机器上仅保存 mirror 到 master 的子图拓扑结构,而 mirror 点并未被实际存储(比如权重值),每台机器负责一部分 master 存储(
)。 如下图所示,Gemini 将图按照 partition 算法切分到 2 个不同的机器。其中 mirror 作为逻辑结构,没有为其分配实际存储空间;但每条边被存储了两次。
优点:单机可以完整获取 master 的拓扑结构,不需要全局维护节点状态。| 表示方法 | 邻接矩阵 | 邻接表 | 十字链表 |
| 优点 | 存储结构简单,访问速度快,顺序遍历边 | 节省空间,访问速度较快 | 在邻接表基础上进一步,节省存储空间。 |
| 缺点 | 占用空间很大(n*n 存储空间) | 存储使用指针,随遍历边结构,为提高效率,需要同时存储出边入边数据。 | 表示很复杂,大量使用了指针,随机遍历边,访问慢。 |
优点:存储紧密,内存局部性强;缺点:遍历边时,需要依赖上一个点的最后一条边的 index,所以只能单线程遍历。压缩矩阵算法无法实时更新拓扑结构,所以压缩矩阵算法只适用静态或者对数据变化不敏感的场景。| CSC 伪代码 | CSR 伪代码 |
| loc← 0 for vi←0 to colmns for idx ←0 to colmn [i] do // 输出到指定行的列 edge [vi][index [idx]] ←value [loc] loc← loc+1 end end | loc← 0 for vi←0 to rows for idx ←0 to row [i] do // 输出到指定列的行 edge [ index [idx]] [vi] ←value [loc] loc← loc+1 end end |
Gemini 改进后的 CSR 算法使用 bitmap 替换 CSR 原有的 Rows 结构:• ext 为 bitmap,代码此 bit 对应的 vid 是否存在出边,如上 id 为 0/2/4 的点存在出边。• nbr 为出边 id;• ndx 表示保存了边的 nbr 的 index 范围;如上图 CSR 图,点 0 存在出边(ext [0] 为 1),通过 idx 的差值计算出 0 点存在一条出边(idx [1]-idx [0]=1),相对于存储 0 点第一条出边的 nbr 的下标为 0(idx [0]);同理可推得点 1 无出边。Gemini 双压缩 CSC 算法将 idx 拆分成 vtx 及 off 两个结构:• vtx 代表存在入边的点集合;• nbr 为入边数组;• Off 表示保存入边 nbr 的 index 偏移范围;如上图 CSC 算法:vtx 数组表示点 1,2,3,5 存在入边,使用 5 个元素的 off 存储每个点的偏移量。如点 2 存在由 0 指向自己的入边 (0ff [2]-off [1]=1), 所以 nbr [1] 存储的就是点 2 的入边 id(0)。优点:通过改进后的存储结构,同时支持多线程并行。
在开始计算前,都需要统计活跃边的数量,确定图模式。在迭代过程中,每一个集群节点只保存部分计算结果。在分布式系统中,消息传播直接涉及到通信量,间接意味着阈值强相关网络带宽和引擎的计算效率。双模式直接平衡了计算负载与通信负载。圆角矩形标识操作是在本地完成的,Gemini 将大量的需通信工作放在本地完成。| 点 s | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| Out Edge | 0 | 3 | 5 | 30 | 2 | 4 | 6 | 2 | 20 |
| 切图轮次 | 1 | 2 | 3 |
| 剩余边 | 72 | 34 | 22 |
| 平均分配 | 18 | 12 | |
| Master 分配结果 | 0: 0~3 | ||
| 1: | 4~6 | ||
| 2: | 7~8 | ||
| 3: |
, 其中平衡因子定义为 α=8*(partitions-1)。 仍然以上图为例,Gemini 通过ɑ因子平衡了边的分布。| 切图轮次 | 1 | 2 | 3 | 4 |
| 剩余权重边 | 288 | 208 | 128 | 44 |
| 平均分配 | 72 | 70 | 64 | 44 |
| Master 分配结果 | 0: 0~2 | |||
| 1: | 3~4 | |||
| 2: | 5~7 | |||
| 3: | 8 |
在 UMA 架构下,所有 cpu 都通过相同的总线以共享的方式访问内存。在物理结构上,UMA 就不利于 cpu 的扩展(总线长度、数据总线带宽都限制 cpu 的上限)。Numa (Non-Uniform Memory Access, 非一致性内存访问)是目前内核设计主流方向。每个 cpu 有独立的内存空间(独享),可通过 QPI(quick path Interconnect)实现互相访问。由于硬件的特性,所以跨 cpu 访问要慢 [11]。
相对于 UMA 来说,NUMA 解决 cpu 扩展,提高数据总线宽度总线长度带来的问题,每个 cpu 都有自己独立的缓存。根据 NUMA 的硬件特性分析,NUMA 具有更高本地内存的访问效率,方便 CPU 扩展。HPC 需要数据访问的高效性,所以 NUMA 架构更适合 HPC 场景(UMA 与 NUMA 无优劣之分)。Gemini 充分利用了 NUMA 对本 socket 内存访问低延迟、高带宽的特性,将本机上的点跨多 socket 数据实现 NUMA-aware 切分(切分单位 CHUNKSIZE)。切分算法参考 Location-aware。
上图列举了集群中 master 分布情况,以 Node0 为例:| 节点 | Node 0 |
| Master 范围 | 0、1 |
| 阶段 1 | 将数据向 Node1 发送关于点 2 的数据,接收来自 Node2 数据 |
| 阶段 2 | 将数据向 Node2 发送关于点 5 的数据,接收来自 Node1 数据 |
| 阶段 3 | 处理自身的数据(本地数据不经网络传输) |
开始计算时,每个 core 均按照自己的 threadstate 进行处理数据,更大提升 cpu 使用效率。该设计是以点为单位进行的数据处理,但未解决热点的难题(这也是业界难题,可以对热点再次切分,也是需要突破的一个问题)。下面是 2 core 的 work stealing 示意图:
其中在初始情况 T0 时刻,core1 与 core2 同时开始执行,工作状态都为 working;在 T1 时刻, core2 的任务首先执行完成,core1 还未完成。为了提高 core2 的利用率,就可以将 core1 的任务分配给 core2 去做。为了避免 core1、core2 访问冲突,此处使用原子操作获取 stealing 要处理 id 范围,处理完成之后,通过 socket 内部写入指定空间。在 T2 时刻,core2 更新工作状态为 stealing,帮助 core1 完成任务。在开源代码中,在构图设计 tune chunks 过程,可以实现跨机器的连续数据块读取,提升跨 socket 的效率。注:开源代码中,push 模式下并未使用到 tread state 结构,所以 tune chunks 中可以省略 push 模式 thread state 的初始化工作。其中在初始情况 T0 时刻,core1 与 core2 同时开始执行,工作状态都为 working;
Gemini 在框架设计中创新的使用 signal、slot。将每轮迭代分为两个阶段:signal(数据发送),slot(消息处理),此处实现了通信与数据处理过程的解耦。
上图代码分段读取文件,统计每个点的出边信息,见 line 456、457,通过 openmpi 通信,聚合所有点出边信息 line 460。Line 451:原理上可以使用 omp 并发,但由于原子操作锁竞争比较大效率并不高。
解释最后一行:owned_vertices 记录当前机器 master 点个数,partition_offset [partition_id] 记录 master 节点 vid 的下限,partition_offset [partition_id+1] 记录 master 节点 vid 的上限。好处:1. 提升了内存的访问效率;2. 减少了内存的零头(在这个过程中,Gemini 为提高内存块读取的效率,使用 pagesize 进行内存对齐。)。
NUMA aware 也进行了 a 因子平衡和 pagesize 对齐。总结:机器机器共享同一份出边统计数据,所以在 location aware 和 numa aware 阶段的结果都是相同的,partition 结果也不会出现冲突的情况。注:aware 阶段都是对 master 的切分,未统计 mirror 的状态;而构图过程是从 mirror 的视角实现的,所以下一个阶段就需要统计 mirror 信息。
单节点处理本地数据(按照 CHUNCKSIZE 大小,分批向集群其他节点分发边数据)。记录 mirror 点的 bitmap 及出边信息。
数据发送过程是按照 CHUNCKSIZE 大小,分批发送。
在发送结束时,需确保所用的数据发送完成,发送字符‘\0‘作为结束符。
在边存储时,数据分发实现了并发传输。代码实现过程,见下图代码注释。
边数据分发过程代码:
处理模块代码定义
注意:line1796 send_queue_mutex 的使用,通过锁控制发送模块的先后顺序。任务调度算法实现:
为保证每台机器上的计算结果一致,所以在传播过程中每个机器都会接收到相同的数据,在进行计算。
在切换双模运算时,调用了 resize 方法,此方法实现:当仅超过 capacity 时,才重新 alloc 内存空间,未实现进行缩容(空间
)。
a• adj_index 会成为系统瓶颈论文中也提到 adj_index 一级索引会占用大部分空间(论文中也提到了会成为瓶颈)。改进后的 CSC 压缩算法使用二级索引结构。在计算时会影响数据访问速度,无向图中压缩效果不好,远高于一级索引的空间复杂度(幂律分布决定,极大部分点存在 1 条以上的出边,易得空间复杂度 2|V’|>|V|)。• α 因子调整α 因子应该根据图的特征进行动态调整,否则很容易造成内存 partition 偏斜。• 动态更新由于压缩矩阵和 partition 方式都限制了图的更新。可通过改变 parition 切分方式,牺牲 numa 特性带来的局部性,通过 snapshot 实现增量图。• 外存扩展Gemini 是共享内存的分布式引擎。在实际生产环境中,通过暴力增加机器解决内存不足的问题,不是最优解。大容量外存不失为更好的解决方案。这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我想为我的Rails网络应用程序提供推荐功能。特别是,我想向新注册的用户推荐他可能想要关注的其他用户。Rails中是否有用于此目的的引擎/gem?如果没有,我应该从哪里开始构建它?谢谢。 最佳答案 有Coletivogemhttps://github.com/diogenes/coletivo我试了一下。在MySQL上运行。Neo4jhttp://neo4j.org真的很容易实现一个“跟随谁”。事实上,大多数展示其能力的样本都涉及“跟随谁”。快速提示-只有在JRuby上运行时,Neo4j.rb才会很酷。如果不是-使用Neograph
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in
给定两个大小相等的数组,如何找到不考虑位置的匹配元素的数量?例如:[0,0,5]和[0,5,5]将返回2的匹配项,因为有一个0和一个5共同;[1,0,0,3]和[0,0,1,4]将返回3的匹配项,因为0有两场,1有一场;[1,2,2,3]和[1,2,3,4]将返回3的匹配项。我尝试了很多想法,但它们都变得相当粗糙和令人费解。我猜想有一些不错的Ruby习惯用法,或者可能是一个正则表达式,可以很好地回答这个解决方案。 最佳答案 您可以使用count完成它:a.count{|e|index=b.index(e)andb.delete_at