在 Python 个进程之间共享内存中的大型数据结构?
Sharing a large data-structure in-memory among Python processes?
我们在一个 Linux 框上有大约 10 Python 个进程 运行,所有进程都读取相同的大型数据结构(恰好是一个 Pandas DataFrame
, 本质上是一个 2D numpy 矩阵).
这些进程必须尽可能快地响应查询,而将数据保存在磁盘上已经不能满足我们的需求了。
我们真正需要的是所有进程都可以完全随机访问内存中的数据结构,这样它们就可以检索执行任意计算所需的所有元素。
由于其大小,我们不能在内存中复制数据结构 10 次(甚至两次)。
有没有办法让所有 10 个 Python 进程共享对内存中数据结构的随机访问?
(COW) on fork()
,数据未被复制
除非它被写入。
因此,如果您在全局命名空间中定义 DataFrame,df
,那么您
可以 访问 它从你希望的随后产生的子进程中,
并且 DataFrame 不需要额外的内存。
仅当其中一个子进程修改 df
(或与 df
相同内存页上的数据)时,才会复制(该内存页上的)数据。
因此,尽管听起来很奇怪,但您无需在 Linux 上执行任何特殊操作即可在子进程之间共享对大型内存中数据结构的访问,除非在全局命名空间中定义数据在 生成子进程之前。
Here is some code 演示写时复制行为。
当数据被修改时,它所在的内存页被复制。
如 this PDF:
中所述
Each process has a page table which maps its virtual addresses to
physical addresses; when the fork() operation is performed, the new
process has a new page table created in which each entry is marked
with a ‘‘copy-on- write’’ flag; this is also done for the caller’s
address space. When the contents of memory are to be updated, the
flag is checked. If it is set, a new page is allocated, the data from
the old page copied, the update is made on the new page, and the
‘‘copy-on-write’’ flag is cleared for the new page.
因此,如果内存页上的某个值有更新,则该页是
复制。如果大型 DataFrame 的一部分驻留在该内存页上,则只会复制该部分,而不是整个 DataFrame。
By default the page size is usually 4 KiB but can be larger depending on how the MMU is configured.
输入
% getconf PAGE_SIZE
4096
查找系统上的页面大小(以字节为单位)。
另一个解决方案有一个相当大的问题,即要求数据帧始终是静态的,并且要求 Linux。
你应该看看 Redis,虽然不如原生 python 内存结构那么快,但在同一台机器上查询 table 类对象时仍然非常快。如果您希望通过共享内存结构获得最高速度,请查看新生的 Apache Arrow 项目。
两者都提供了动态数据功能和跨语言支持的可观优势,而 Redis 还允许您使用多节点。
我们在一个 Linux 框上有大约 10 Python 个进程 运行,所有进程都读取相同的大型数据结构(恰好是一个 Pandas DataFrame
, 本质上是一个 2D numpy 矩阵).
这些进程必须尽可能快地响应查询,而将数据保存在磁盘上已经不能满足我们的需求了。
我们真正需要的是所有进程都可以完全随机访问内存中的数据结构,这样它们就可以检索执行任意计算所需的所有元素。
由于其大小,我们不能在内存中复制数据结构 10 次(甚至两次)。
有没有办法让所有 10 个 Python 进程共享对内存中数据结构的随机访问?
fork()
,数据未被复制
除非它被写入。
因此,如果您在全局命名空间中定义 DataFrame,df
,那么您
可以 访问 它从你希望的随后产生的子进程中,
并且 DataFrame 不需要额外的内存。
仅当其中一个子进程修改 df
(或与 df
相同内存页上的数据)时,才会复制(该内存页上的)数据。
因此,尽管听起来很奇怪,但您无需在 Linux 上执行任何特殊操作即可在子进程之间共享对大型内存中数据结构的访问,除非在全局命名空间中定义数据在 生成子进程之前。
Here is some code 演示写时复制行为。
当数据被修改时,它所在的内存页被复制。 如 this PDF:
中所述Each process has a page table which maps its virtual addresses to physical addresses; when the fork() operation is performed, the new process has a new page table created in which each entry is marked with a ‘‘copy-on- write’’ flag; this is also done for the caller’s address space. When the contents of memory are to be updated, the flag is checked. If it is set, a new page is allocated, the data from the old page copied, the update is made on the new page, and the ‘‘copy-on-write’’ flag is cleared for the new page.
因此,如果内存页上的某个值有更新,则该页是 复制。如果大型 DataFrame 的一部分驻留在该内存页上,则只会复制该部分,而不是整个 DataFrame。 By default the page size is usually 4 KiB but can be larger depending on how the MMU is configured.
输入
% getconf PAGE_SIZE
4096
查找系统上的页面大小(以字节为单位)。
另一个解决方案有一个相当大的问题,即要求数据帧始终是静态的,并且要求 Linux。
你应该看看 Redis,虽然不如原生 python 内存结构那么快,但在同一台机器上查询 table 类对象时仍然非常快。如果您希望通过共享内存结构获得最高速度,请查看新生的 Apache Arrow 项目。
两者都提供了动态数据功能和跨语言支持的可观优势,而 Redis 还允许您使用多节点。