Python的写时复制或访问时复制共享内存

3

我试图理解进程间共享内存的工作原理,但卡住了。
我正在使用一个非常简单的测试程序 c.py 并使用 smem 跟踪内存。

c.py:

import sys
import time
from multiprocessing import Process

arr = [x for x in range(int(1e6) * 50)]
print(sys.getsizeof(arr))  # 411943896

def f():
    x = 0
    for i in range(len(arr)):
        #x += arr[i]
        pass
    time.sleep(10)

p = Process(target=f)
p.start()
p.join()

当我将 x += arr[i] 注释掉后运行程序,会看到以下结果:

PID User     Command                         Swap      USS      PSS      RSS
  1693779 1000     python /usr/bin/smem -n -t         0     8368     9103    14628
  1693763 1000     python c.py                        0     1248   992816  1986688
  1693749 1000     python c.py                        0     1244   993247  1989752
  -------------------------------------------------------------------------------
      3 1                                           0    10860  1995166  3991068

如果我理解正确,PSS 告诉我我的单个全局数组“arr”在两个进程之间共享,而 USS 显示每个进程分配的唯一内存很少。然而,当我取消注释子进程中的“x + = arr [i]”——仅访问数组元素——就会产生非常不同的结果:
PID User     Command                         Swap      USS      PSS      RSS
  1695338 1000     python /usr/bin/smem -n -t         0     8476     9508    14392
  1695296 1000     python c.py                       64  1588472  1786582  1986708
  1695280 1000     python c.py                        0  1588644  1787246  1989520
  -------------------------------------------------------------------------------
      3 1                                          64  3185592  3583336  3990620

我不理解,似乎是访问数组导致它被复制到子进程中,这意味着Python实际上是在访问时复制共享内存,而不是在写入时。

  1. 我的理解是否正确?当全局变量arr被访问时,存储 arr 数据的内存是否已被复制到子进程中?

  2. 如果是这样,那么子进程有没有办法在不加倍内存使用的情况下访问全局变量?

  3. 我希望有人能够解释smem报告中的整体内存使用情况,但在这种情况下,我认为这是一个更适合SU的问题。如果只进行简单的复制,我会期望内存翻倍,但是每个进程都显示唯一的内存1588472,在此基础上总的PSS共享内存为2x 1786582,因此总共约为6750108?我相当确定我的理解是非常错误的,但我不知道如何解释它。

1个回答

3

你正在操作元素。Python的标准实现使用了引用计数,即使是查看对象也需要对其引用计数进行写操作。


我不认为我理解了。你的意思是操作 x = x + arr[1] 会增加对象 arr 的引用计数吗? - zrf
@zrf:你是否将其视为类似于C风格的连续内存数组?Python列表由指向对象的指针数组支持,每个对象都有自己的引用计数。 - user2357112
是的,我想我当时就是这样想的。现在开始更加有意义了。我认为引用计数器保存在与对象相同的内存区域中,这就是触发数组复制的原因。 - zrf
3
请看此链接:https://instagram-engineering.com/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf - Igor Karbachinsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接