来自 concurrent.futures 的 RSS 内存使用情况

RSS memory usage from concurrent.futures

我有一个简单的脚本试图强调 concurrent.futures 库,如下所示:

#! /usr/bin/python

import psutil
import gc
import os
from concurrent.futures import ThreadPoolExecutor

WORKERS=2**10

def run():
        def x(y):
                pass

        with ThreadPoolExecutor(max_workers=WORKERS) as pool:
                for _ in pool.map(x, [i for i in range(WORKERS)]):
                        pass

if __name__ == '__main__':
        print('%d objects' % len(gc.get_objects()))
        print('RSS: %s kB' % (psutil.Process(os.getpid()).get_memory_info().rss / 2**10))
        run()
        print('%d objects' % len(gc.get_objects()))
        print('RSS: %s kB' % (psutil.Process(os.getpid()).get_memory_info().rss / 2**10))

最终在 2 核 linux 机器上产生以下输出 运行 python 2.7:

# time ./test.py
7048 objects
RSS: 11968 kB
6749 objects
RSS: 23256 kB

real    0m1.077s
user    0m0.875s
sys     0m0.316s

虽然这是一个有点人为的例子,但我很难理解为什么 RSS 在这种情况下会增加,以及分配的内存用于什么。

Linux 应该使用 COW 很好地处理分叉内存,但由于 CPython 是引用计数的,因此继承内存的部分不会真正只读,因为需要更新引用。考虑到引用计数的开销是多么小,12MB 的增加让我感到惊讶。如果我不使用 ThreadPoolExecutor 而只是使用 threading 库生成守护线程,RSS 只会增加 4MB。

我目前还不清楚是怀疑 CPython 分配器还是 glibc 分配器,但我的理解是后者应该可以处理这种并发风格,并且能够重用 arena 来跨生成的分配器进行分配线程。

我在 python 2.7.9 下使用 concurrent.futures 3.0.3 的向后移植版本,在 4.1 内核上使用 glibc 2.4。任何有关如何进一步调查此问题的建议或提示将不胜感激。

我建议您阅读来自

的回复

正如他所说,您可以生成的线程数取决于您的线程是否执行任何 I/O 操作。如果是这样,有一些方法可以优化这个问题。如果不是我通常做 MAX_THREADS = N_CORES + 1.

不确定,但您是否尝试在一个核心上生成 1024 个线程?

大多数内存分配器不会return 所有它们的内存到OS。

尝试调用 run() 两次并在第二次检查 RSS before/after。

(也就是说,荒谬的线程数通常不是一个好主意)