论文名称:PIM-tree: A Skew-resistant Index for Processing-in-Memory
摘要
当今的内存索引性能受到内存延迟/带宽瓶颈的限制。Processing-in-memory (PIM) 是一种新兴的方法,可能通过实现低延迟内存访问,其聚合内存带宽随 PIM 节点数量扩展,来缓解这种瓶颈。然而,在工作负载偏斜的情况下,PIM 系统在最小化节点间通信和实现负载平衡之间存在固有的张力。本文介绍了 PIM-tree,一种针对 PIM 系统的有序索引,它通过在数据和查询中实现加载平衡,实现了低通信成本和高负载平衡。我们的抗偏移索引基于主机 CPU 和 PIM 节点之间的劳动分工,利用各自的优势。我们引入了推-拉搜索,它可以根据工作负载的偏移程度动态地决定是将查询推送到 PIM-tree 节点还是将节点的键拉回到 CPU。结合其他 PIM-friendly 优化(如阴影子树和分块跳表),我们的 PIM-tree 可以为点查询、更新和范围扫描提供高吞吐量、(保证的)低通信成本和(保证的)高负载平衡。我们在最新的UPMEM PIM 系统上实现了 PIM-tree,除了先前提议的 PIM 索引外,该系统具有32个 CPU 核心和 2048 个 PIM 节点。在具有 5 亿个键和 100 万个查询批次的工作负载下,使用 PIM-tree 的吞吐量比两个最佳的先前基于 PIM 的方法高达 69.7× 和 59.1×。据我们所知,这是第一次在真正的 PIM 系统上实现有序索引。
1 引言
CPU 速度和内存速度之间的不匹配(即“内存墙”)使得内存访问成为当今数据密集型应用程序中的主要成本。传统的架构使用多级缓存来减少 CPU 和内存之间的数据移动,但如果应用程序表现出有限的局部性,大多数数据访问仍然由内存服务。这种过度的数据移动会产生巨大的能量成本,并且性能受到高内存延迟和/或有限内存带宽的限制。处理-in-memory (PIM) [25, 30],也称为近数据处理,正在成为减少昂贵数据移动的关键技术。通过将计算资源集成在内存模块中,PIM 可以使数据密集计算在启用 PIM 的内存模块中执行,而不是将所有数据移动到 CPU 来进行处理。最近的研究表明,对于高数据密度和低缓存局部性的程序,PIM 可以通过减少数据移动来提高性能并降低功耗 [14, 15]。尽管处理-in-memory/processing-in-storage的提议可追溯至至少1970年[29],包括数据库社区在主动磁盘[26]中的尝试,但由于3D堆叠内存[18, 22]的进步以及最近商业PIM系统原型的可用性[30],PIM正在成为一种关键技术。典型的利用现代PIM体系结构的应用程序包括神经网络[3, 21, 23, 32]、图形处理[1, 17, 35]、数据库[6, 7]、稀疏矩阵乘法[13, 33]和基因组分析[2, 34]。
PIM 系统通常组织为一个主机(多核)CPU,它将计算任务推送到一组 PIM 模块(增强计算内存模块),并收集结果。因此,需要移动任务描述符和数据,这两个成本之和是 CPU 和 PIM 模块之间的通信成本。主机 CPU 可以是任何商品多核处理器,通常比 PIM 模块内的微小 CPU 更强大。因此,PIM 系统的一个有趣特征是潜在地利用两组资源(CPU 端和 PIM 端)。
在本文中,我们专注于为内存数据设计一种适合 PIM 的有序索引。有序索引(例如 B 树[11])是数据库/数据存储的支柱组件之一,支持高效的搜索查询、范围扫描、插入和删除。以前针对 PIM [10, 24] 的工作提出了基于范围划分的有序索引:将键空间划分为 𝑃 个相等键数的子范围,每个 𝑃 PIM 模块存储一个子范围。每个 PIM 模块在其子范围内维护一个本地索引,而主机 CPU 则维护索引顶部直到局部索引的 𝑃 个根。这种方法适用于具有均匀随机键的数据和查询,即这些工作所研究的设置,但在数据或查询偏斜下容易出现负载不平衡。在更实际的工作负载中,查询/更新批次可能集中在少量分区的数据上,压倒了这些 PIM 模块,而其他模块处于空闲状态。在极端情况下,只有一个 PIM 模块处理查询,其余的模块处于空闲状态,在单个(微弱的)处理器上完全串行化整个查询批次。该方法还需要为保持分区(大致)平衡的所有数据移动付出代价。在最近的一篇论文[19]中,我们设计了一种 PIM-friendly 跳表,它在渐进意义下实现了负载平衡(详见第2.4节),但该解决方案不实用(如下所述)。
为了解决查询和数据偏斜的挑战,我们提出了 PIM-tree,一种实用的面向 PIM 的有序索引,它可以实现在数据和查询的任何偏斜程度下实现低通信成本和高负载平衡。我们的抗偏移索引基于主机 CPU 和 PIM 节点之间的新颖劳动分工,利用各自的优势。此外,它结合了 B+-树和跳表的特点来实现其目标。我们专注于以批处理同步的方式实现高吞吐量,处理查询批次。PIM-tree 支持广泛的批并行操作,包括点查询(Get,Predecessor)、更新(Insert,Delete)和范围扫描。
2 背景
2.1 PIM 系统架构和模型
处理内存中的模型。我们使用处理内存中的模型(PIM Model)(首次描述于[19])作为通用 PIM 系统的抽象。它包括一个主机 CPU 前端(CPU 侧)和一组 𝑃 个 PIM 模块(PIM 侧)。CPU 侧是一个标准的多核处理器,具有 𝑀 个字的片上缓存。每个 PIM 模块由一个 DRAM 存储器(本地 PIM 存储器)、一台片上处理器(PIM 处理器)和 Θ(𝑛/𝑃) 个字的本地存储器(其中 𝑛 表示问题的规模)组成。PIM 处理器简单但通用(例如,能够运行 C 代码的单一按顺序核心)。主机 CPU 可以向 PIM 模块发送代码、启动代码,并检测代码完成的情况。它还可以向 PIM 存储器发送数据并接收来自 PIM 存储器的数据。该模型假设没有直接的 PIM 对 PIM 通信,尽管在支持此类通信的 PIM 系统上我们可以利用这种通信。
由于 PIM 模型结合了共享内存侧(CPU 和其缓存)和分布式侧(PIM 模块),因此算法使用共享内存指标(工作量、深度)和分布式指标(本地工作量、通信时间)进行分析。在 CPU 侧,该模型考虑了 CPU 工作量(所有核心的总工作量)和 CPU 深度(关键路径上的所有工作)。在 PIM 侧,该模型考虑了 PIM 时间,即任一 PIM 核上的最大本地工作量,以及 IO 时间,即任一 PIM 模块发送/接收的最大消息数。程序以批量同步轮次执行[31],算法的整体复杂度指标是每一轮复杂度指标的总和。本文重点关注 IO 时间和 IO 轮次。
编程接口。为了具体起见,我们假设了我们通用 PIM 系统的以下编程接口,尽管我们的技术也可以与其他接口一起使用。程序由两部分组成:在主机 CPU 上执行的主机程序和在 PIM 模块上执行的 PIM 程序。主机程序具有附加功能(下文讨论),用于与 PIM 侧进行通信,包括调用 PIM 模块上的 PIM 程序和向 PIM 模块传输数据/从 PIM 模块接收数据的功能。PIM 程序是一个传统程序,在由主机程序启动时在所有 PIM 处理器上执行。它使用模块的本地存储器执行,对 CPU 侧或其他 PIM 模块没有可见性。具体函数为(命名为 MPI 风格[16]):
- PIM_Load(PIM_Program_Binary):将二进制文件加载到 PIM 模块。
- PIM_Launch():在所有 PIM 上启动加载的 PIM 程序。
- PIM_Status():检查 PIM 程序在所有 PIM 上是否已完成。
- PIM_Broadcast(src, length, PIM_Local_Address):将固定长度的缓冲区复制到每个 PIM 的相同本地存储器地址。
- PIM_Scatter(srcs[], length[], PIM_Local_Address):类似于 PIM_Broadcast,但每个 PIM 模块都有一个独立的缓冲区。
- PIM_Gather(dsts[], length[], PIM_Local_Address):与 PIM_Scatter 的相反操作,将读入缓冲区数组 dsts[]。
基于这个接口,我们的 PIM-friendly 有序索引以批量同步轮次处理操作批,如[28]中的步骤一样,使用算法1中的步骤。如第5节所讨论的,当实现我们的 PIM-friendly 程序时,我们使用流水线技术重叠 CPU 的步骤1和 PIM 模块的步骤3。
具体案例:UPMEM。我们在 UPMEM 的最新 PIM 系统[30]上评估了我们的技术。UPMEM 的架构(图1)是实例化 PIM 模型的一种方式。它的 PIM 模块是即插即用的 DRAM DIMM 替代品,因此可以配置为各种传统 DRAM 存储器和 PIM 设备的比例(目前最大可用配置有2560个 PIM 模块)。CPU 可以访问主存储器(传统 DRAM)和所有 PIM 存储器,但每个 PIM 处理器只能访问其本地存储器。每个 PIM 模块具有高达628 MB/s的本地 DRAM 带宽,因此配备2560个 PIM 模块的机器可以提供高达1.6 TB/s的总带宽[15]。为了在 PIM 模块之间传输数据,CPU 从源读取并写入目标。UPMEM 的 SDK 支持上述编程接口函数,但有一个限制,即散射/聚集函数必须传输相同长度的缓冲区到/从所有 PIM 模块(即,缓冲区被填充到相等长度)。UPMEM 的主存储器(PIM 模型中没有的一个组件)能够运行具有 CPU 侧内存占用超过 𝑀 个字的程序,但这些额外的内存访问带来了另一种类型的通信,即在 PIM 模型中不存在的 CPU-DRAM 通信。因此,对于主机程序来说,缓存效率非常重要。我们在 PIM-tree 中的解决方案是仅使用少量的 CPU 侧内存:Θ(𝑆) < 𝑀 个字,用于 𝑆 个操作的批处理。
UPMEM PIM系统的架构,这是我们通用PIM系统架构的一个具体例子。PIM模块被打包到通过普通内存通道连接到主机CPU的内存内存中。CPU端还包括传统的DRAM模块,这不是PIM模型的一部分。
2.2 负载平衡初步 PIM
系统面临的主要挑战之一是保持 PIM 模块之间的负载平衡,我们将其定义如下:
定义 2.1. 如果每个 PIM 程序执行的工作量(单位时间指令)为𝑂(𝑊 /𝑃),并且每个 PIM 模块发送/接