首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当内存中有巨大(33 is )数据结构(没有交换)时,Python继续运行10+mins (在程序中的最后一条语句之后)。

当内存中有巨大(33 is )数据结构(没有交换)时,Python继续运行10+mins (在程序中的最后一条语句之后)。
EN

Stack Overflow用户
提问于 2020-08-10 22:08:57
回答 1查看 185关注 0票数 5

我需要解析一个巨大的gz文件(大约10 gz压缩,100 gz未压缩)。代码在内存中创建数据结构('data_struct')。我在一台带有16个CPU和大量内存(即Intel(R) Xeon(R) CPU E5-2667 v4 @ 3.20GHz 200+ GB)的计算机上运行,运行CentOS6.9。我使用Python3.6.3 (CPython)中的一个类实现了这些东西,如下所示:

代码语言:javascript
复制
class my_class():
    def __init__(self):
        cmd = f'gunzip huge-file.gz'
        self.process = subprocess(cmd, stdout=subprocess.PIPE, shell=True)
        self.data_struct = dict()

    def populate_struct(self):
        for line in process.stdout:
            <populate the self.data_struct dictionary>
        
    def __del__():
        self.process.wait()
        #del self.data_struct  # presence/absence of this statement decreases/increases runtime respectively
#================End of my_class===================

def main():
    my_object = my_class()
    my_object.populate_struct()
    print(f'~~~~ Finished populate_struct() ~~~~')  # last statement in my program.
    ## Python keeps running at 100% past the previous statement for 10+mins

if __name__ == '__main__':
    main()
#================End of Main=======================

我的data_struct在内存中的驻留内存消耗(仅内存,不交换)约为33 no。我使用$ top来查找process的PID,并使用$ strace -p <PID> -o <out_file>跟踪process (查看Python正在做什么)。在执行populate_struct()时,我可以在strace的out_file中看到Python正在使用mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b0684160000之类的调用来创建data_struct。当Python运行通过上一个print()语句时,我发现Python只执行munmap()操作,如下所示:

代码语言:javascript
复制
munmap(0x2b3c75375000, 41947136)        = 0
munmap(0x2b3c73374000, 33558528)        = 0
munmap(0x2b4015d2a000, 262144)          = 0
munmap(0x2b4015cea000, 262144)          = 0
munmap(0x2b4015caa000, 262144)          = 0
munmap(0x2b4015c6a000, 262144)          = 0
munmap(0x2b4015c2a000, 262144)          = 0
munmap(0x2b4015bea000, 262144)          = 0
munmap(0x2b4015baa000, 262144)          = 0
...
...

在上一次10+语句之后,Python在print() mins到12分钟之间一直运行。一个观察是,如果我在del self.data_struct方法中有__del__()语句,那么只需要2分钟。我已经做了多次这些实验,运行时由于del self.data_struct__del__()中的存在/缺失而减少/增加。

我的问题:

  1. I理解的是,Python正在使用munmap()进行清理工作,但与munmap()不同的是,其他语言(如munmap())会立即释放内存并退出程序。如上所示,通过实现,我做得对吗?有什么方法可以告诉munmap()
  2. Is避免这个munmap()吗?
  3. 为什么在__del__()没有del self.data_struct语句的情况下需要10+mins进行清理,如果__del__()中有del self.data_struct语句,只需要2分钟就可以清除?
  4. 有办法加快清理工作--
  5. 有一种方法可以在不进行清理工作的情况下立即退出程序吗?H 231G 232

其他关于解决这个问题的想法/建议是值得赞赏的。

EN

Stack Overflow用户

发布于 2020-08-10 23:47:25

请尝试一个最新版本的Python (至少3.8)?这显示出几个温和的迹象(!)CPython的对象释放器中最坏情况的二次时间算法的形式,它在这里重写了(请注意,链接到这里的问题反过来包含一个链接到一个较旧的StackOverflow帖子,其中包含更多的细节):

https://bugs.python.org/issue37029

光彩照人

如果我的猜测是正确的,那么内存量并不是特别重要--而是由CPython的“小对象分配器”(obmalloc.c)管理的大量不同的Python对象,以及它们释放内存的顺序中的“坏运气”。

当第一次编写这段代码时,RAM不够大,无法容纳数百万Python对象,因此没有人注意到,去分配逻辑的一个特定部分在分配的"arenas“数量中需要时间平方(细节并不是真正有用的,但"arenas”是进行系统mmap()munmap()调用的粒度--256个KiB块)。

并不是那些映射调用占用了大量时间,任何使用OS内存映射工具的语言的适当实现最终都会调用munmap()足够多的时间来释放其mmap()调用所消耗的操作系统资源。

那是条红鲱鱼。munmap()被多次调用仅仅是因为您分配了许多对象,这需要很多mmap()调用。

没有任何明确或简单的方法来解释问题何时出现。请参阅上面的“厄运”;-) CPython 3.8的相关代码被重写为最坏的线性时间,这为触发问题报告的特定程序提供了大约250个加速因子(参见已经给出的链接)。

正如一条注释所指出的,您可以通过调用os._exit()在任何时候立即退出您的程序,但是前面的下划线是为了吓跑您:“立即”的意思是“立即”。不执行任何类型的清理。例如,您的类中的__del__方法?跳过了。__del__是作为去分配的副作用运行的,但是如果您实际上“立即释放内存并退出程序”,那么就不会运行任何类型的析构函数,也不会运行任何在atexit模块中注册的处理程序等等。它就像程序死亡一样激烈,例如,存在分段错误。

票数 4
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63348685

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档