Plasma 内存对象存储
已发布 2017 年 8 月 8 日
作者 Philipp Moritz 和 Robert Nishihara
Philipp Moritz 和 Robert Nishihara 是加州大学伯克利分校的研究生。
Plasma:高性能共享内存对象存储
Plasma 的动机
这篇博文介绍了 Plasma,一个作为 Apache Arrow 一部分开发的内存对象存储。Plasma 将不可变对象保存在共享内存中,以便许多客户端可以跨进程边界高效地访问它们。 鉴于向更大、更多核机器发展的趋势,Plasma 在大数据领域实现了关键的性能优化。
Plasma 最初是作为 Ray 的一部分开发的,最近已迁移到 Apache Arrow,希望它能得到广泛应用。
Apache Arrow 的目标之一是充当一个通用数据层,实现多个框架之间零拷贝数据交换。这一愿景的一个关键组成部分是使用堆外内存管理(通过 Plasma)来存储和共享 Arrow 序列化的对象。
昂贵的序列化和反序列化以及数据复制是分布式计算中常见的性能瓶颈。 例如,一个希望将计算分配给多个 Python “工作进程”然后在一个 “驱动” 进程中聚合结果的基于 Python 的执行框架,可能会选择使用内置的 pickle 库来序列化数据。假设每个核心一个 Python 进程,每个工作进程都必须复制和反序列化数据,从而导致内存使用过度。然后,驱动进程将不得不反序列化来自每个工作进程的结果,从而形成瓶颈。
使用 Plasma 和 Arrow,正在操作的数据将被放入 Plasma 存储一次,所有工作进程都可以读取数据,而无需复制或反序列化(工作进程会将相关内存区域映射到它们自己的地址空间)。然后,工作进程将计算结果放回 Plasma 存储中,驱动程序可以读取并聚合这些结果,而无需复制或反序列化数据。
Plasma API
下面我们演示 API 的一个子集。C++ API 在 这里 有更完整的文档,Python API 在 这里 有文档。
对象 ID: 每个对象都与一个字节字符串相关联。
创建对象: 对象在 Plasma 中分两个阶段存储。首先,对象存储通过为其分配缓冲区来创建对象。此时,客户端可以向缓冲区写入数据并在分配的缓冲区内构建对象。当客户端完成时,客户端封存缓冲区,使对象不可变并可供其他 Plasma 客户端使用。
# Create an object.
object_id = pyarrow.plasma.ObjectID(20 * b'a')
object_size = 1000
buffer = memoryview(client.create(object_id, object_size))
# Write to the buffer.
for i in range(1000):
buffer[i] = 0
# Seal the object making it immutable and available to other clients.
client.seal(object_id)
获取对象: 对象被封存后,任何知道对象 ID 的客户端都可以获取该对象。
# Get the object from the store. This blocks until the object has been sealed.
object_id = pyarrow.plasma.ObjectID(20 * b'a')
[buff] = client.get([object_id])
buffer = memoryview(buff)
如果对象尚未被封存,则对 client.get 的调用将阻塞,直到对象被封存。
排序应用
为了说明 Plasma 的优势,我们展示了对一个大型 pandas DataFrame(十亿个条目)进行排序时实现的 11 倍加速(在一台具有 20 个物理核心的机器上)。基线是内置的 pandas 排序函数,它在 477 秒内对 DataFrame 进行排序。为了利用多核,我们实现了以下标准的分布式排序方案。
- 我们假设数据被划分为 K 个 pandas DataFrame,并且每个 DataFrame 已经存在于 Plasma 存储中。
- 我们对数据进行子采样,对子采样的数据进行排序,并使用结果来定义 L 个不重叠的桶。
- 对于 K 个数据分区中的每一个和 L 个桶中的每一个,我们找到落入该桶的数据分区的子集,并对该子集进行排序。
- 对于 L 个桶中的每一个,我们将落入该桶的 K 个已排序的子集收集起来。
- 对于 L 个桶中的每一个,我们合并相应的 K 个已排序的子集。
- 我们将每个桶转换为一个 pandas DataFrame 并将其放入 Plasma 存储中。
使用这种方案,我们可以在 44 秒内对 DataFrame 进行排序(数据从 Plasma 存储开始并在其中结束),与基线相比,速度提高了 11 倍。
设计
Plasma 存储作为一个独立进程运行。它使用 C++ 编写,设计为一个基于 Redis 事件循环库的单线程事件循环。plasma 客户端库可以链接到应用程序中。客户端通过使用 Google Flatbuffers 序列化的消息与 Plasma 存储通信。
征集贡献
Plasma 仍在开发中,API 目前不稳定。目前 Plasma 主要在 Ray 中用作 Arrow 序列化对象的内存缓存。我们正在寻找更广泛的使用案例来帮助完善 Plasma 的 API。此外,我们正在寻求在各个领域的贡献,包括提高性能和构建其他语言绑定。如果您有兴趣参与该项目,请告知我们。