python - numpy如何避免gc引用计数对子进程的访问复制
问题描述
在 POSIX 系统上,在你 fork() 之后,数据只能在你写入子进程后复制到子进程(写时复制)。但是由于python将引用计数存储在对象头中,所以每次在子进程中迭代一个列表时,它都会将其复制到它的内存中。
使用列表和其他数据结构进行测试,我可以断言该行为,以及核心开发人员的一些证实: https ://github.com/python/cpython/pull/3705#issuecomment-420201071
但是在用 numpy 数组测试之后,这并没有发生。
import ctypes
import os
import numpy as np
import psutil
def sharing_with_numpy():
ppid = os.getpid()
print(f'\nSystem used memory: {int(psutil.virtual_memory().used / (1024 * 1024))} MB')
big_data = np.array([[item, item] for item in list(range(10000000))])
print(f'\nSystem used memory: {int(psutil.virtual_memory().used / (1024 * 1024))} MB')
print(ctypes.c_long.from_address(id(big_data)).value)
ref1 = big_data[0]
ref2 = big_data[0]
print(ctypes.c_long.from_address(id(big_data)).value)
print(f'\nSystem used memory: {int(psutil.virtual_memory().used / (1024 * 1024))} MB')
for i in range(5):
if ppid == os.getpid():
os.fork()
for x in big_data:
pass
print(f'\nSystem used memory: {int(psutil.virtual_memory().used / (1024 * 1024))} MB')
if __name__ == "__main__":
sharing_with_numpy()
输出:
System used memory: 163 MB # before array allocation
System used memory: 318 MB # after array allocation
1 # reference count of the array
3 # reference count of the array
System used memory: 318 MB # before fork()
System used memory: 324 MB # after fork() and loop to reference array
System used memory: 328 MB # after fork() and loop to reference array
System used memory: 329 MB # after fork() and loop to reference array
System used memory: 331 MB # after fork() and loop to reference array
System used memory: 340 MB # after fork() and loop to reference array
System used memory: 342 MB # after fork() and loop to reference array
如您所见,内存增加了,但只是略微增加,这表明整个数组没有被复制。
我一直试图了解没有运气发生了什么,你能解释一下吗?谢谢
解决方案
numpy
数组有一个对象头,其中包含一个指向底层数据的指针,单独分配。数据本身没有任何引用计数,因此仅通过读取它不会被修改。
由于numpy
数组是批量分配的,因此用于支持数据存储的较大分配不是来自对象标头来自的小对象池(它们通常是直接从操作系统批量分配的,通过mmap
[*NIX] 或VirtualAlloc
[Windows] ,而不是从在许多分配中细分的内存堆中分配的)。由于它们不与任何引用计数的页面共享页面(它们是原始 C 类型,而不是int
具有自己的对象头的 Python 或类似类型),因此这些页面永远不会被写入,因此永远不会被复制。
推荐阅读
- c# - Visual Studio 配色方案
- c# - 如何在c#中将json转换为十六进制
- node.js - “在附近解析时 JSON 输入意外结束”错误:npm install -g@angular/cli
- python - 如何使用python将鼠标光标放在屏幕中央
- javascript - 删除记录后剑道网格不更新
- git - 以下命令如何使我处于分离的头部状态?
- html - 用户如何知道 html 文档的一些元数据
- firebase - RxDart 的 dart-flutter 中的 Concanete 2 firestore QuerySnapShot 流
- html - 使用跨度作为“按钮”是不好的做法吗?W3Schools 正在这样做
- c - 为什么较小的堆栈边界不会发生分段错误?