Python numpy如何从gc引用计数中避免子进程访问时复制

Python numpy如何从gc引用计数中避免子进程访问时复制,python,numpy,memory-management,fork,Python,Numpy,Memory Management,Fork,在POSIX系统上,在fork()之后,数据只应在写入子进程(写入时复制)后复制到该子进程。 但由于python将引用计数存储在对象头中,因此每次在子进程中迭代列表时,它都会将其复制到内存中 通过测试列表和其他数据结构,我可以断言这种行为,也可以从核心开发人员那里得到一些佐证: 但在使用numpy阵列进行测试后,这种情况并未发生 导入ctypes 导入操作系统 将numpy作为np导入 导入psutil def与_numpy()共享_: ppid=os.getpid() 打印(f'\n系统使用

在POSIX系统上,在fork()之后,数据只应在写入子进程(写入时复制)后复制到该子进程。 但由于python将引用计数存储在对象头中,因此每次在子进程中迭代列表时,它都会将其复制到内存中

通过测试列表和其他数据结构,我可以断言这种行为,也可以从核心开发人员那里得到一些佐证:

但在使用numpy阵列进行测试后,这种情况并未发生

导入ctypes
导入操作系统
将numpy作为np导入
导入psutil
def与_numpy()共享_:
ppid=os.getpid()
打印(f'\n系统使用的内存:{int(psutil.virtual_memory().used/(1024*1024))}MB')
big_data=np.array([[item,item]表示列表中的项目(范围(10000000)))
打印(f'\n系统使用的内存:{int(psutil.virtual_memory().used/(1024*1024))}MB')
打印(ctypes.c_long.from_地址(id(大数据)).value)
ref1=大数据[0]
ref2=大数据[0]
打印(ctypes.c_long.from_地址(id(大数据)).value)
打印(f'\n系统使用的内存:{int(psutil.virtual_memory().used/(1024*1024))}MB')
对于范围(5)中的i:
如果ppid==os.getpid():
os.fork()
对于大数据中的x:
通过
打印(f'\n系统使用的内存:{int(psutil.virtual_memory().used/(1024*1024))}MB')
如果名称=“\uuuuu main\uuuuuuuu”:
与_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类型,不是Python
int
s或具有自己的对象头的类似类型),因此这些页面永远不会被写入,因此也永远不会被复制。

好的,这是有意义的,它检查出来了,谢谢!