加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python管理大变量的分配/释放的策略是什么?

发布时间:2020-12-17 17:38:48 所属栏目:Python 来源:网络整理
导读:作为this question的后续版本,在(C)Python中似乎对小变量和大变量有不同的分配/取消分配策略. 更确切地说,对象大小似乎存在一个边界,在该边界上可以将分配的对象使用的内存返还给OS.低于此大小,不会将内存还给操作系统. 引用从Numpy策略中释放内存的答案: T

作为this question的后续版本,在(C)Python中似乎对小变量和大变量有不同的分配/取消分配策略.
更确切地说,对象大小似乎存在一个边界,在该边界上可以将分配的对象使用的内存返还给OS.低于此大小,不会将内存还给操作系统.

引用从Numpy策略中释放内存的答案:

The exception is that for large single allocations (e.g. if you create a multi-megabyte array),a different mechanism is used. Such large memory allocations can be released back to the OS. So it might specifically be the non-numpy parts of your program that are producing the issues you see.

确实,这两种分配策略很容易显示.例如:

>第一种策略:不将内存返还给操作系统

import numpy as np
import psutil
import gc

# Allocate  array
x = np.random.uniform(0,1,size=(10**4))

# gc
del x
gc.collect()
# We go from 41295.872 KB to 41295.872 KB
# using psutil.Process().memory_info().rss / 10**3; same behavior for VMS

=>没有内存返回给操作系统

>第二种策略:释放的内存被分配给操作系统

当做相同的实验,但数组更大时:

x = np.random.uniform(0,size=(10**5))

del x
gc.collect()
# We go from 41582.592 KB to 41017.344 KB

=>内存已释放到操作系统

似乎使用第二种策略分配了大约大于8 * 10 ** 4字节的对象.

所以:

>是否记录了这种行为? (以及分配策略更改的确切边界是什么?)
>这些策略的内部原理是什么(不仅仅是假设使用mmap / munmap将内存释放回操作系统)
>这是Python运行时100%完成的还是Numpy是否有特定的处理方式? (numpy doc提到了在内存分配器之间切换的NPY_USE_PYMEM)

最佳答案
您观察到的不是CPython的策略,而是CPython版本正在使用的C运行时附带的内存分配器的策略.

当CPython通过malloc / free分配/释放内存时,它不会直接与底层操作系统通信,而是通过内存分配器的具体实现进行通信.在我的Linux上,它是the GNU Allocator.

GNU分配器具有不同的所谓的竞技场,在这种竞技场中,内存不返回给操作系统,而是保留下来,因此无需与操作系统进行通讯即可重复使用.但是,如果请求大量内存(无论“大”的定义如何),分配器就不会使用来自arenas的内存,而是从OS请求内存,因此一旦释放就可以直接将其返回给OS.叫.

CPython有自己的内存分配器-pymalloc,它在C-runtime-allocator的顶部构建.它针对居住在特殊舞台上的小物体进行了优化.与基础C-runtime-allocator相比,创建/释放这些对象时的开销较小.但是,大于512字节的对象不使用此领域,而是由C-runtime-allocator直接管理.

对于numpy的数组,情况甚至更加复杂,因为元数据(例如形状,数据类型和其他标志)和实际数据本身使用了不同的内存分配器:

>对于元数据PyArray_malloc,使用CPython的内存分配器(即pymalloc).
>对于数据本身,使用PyDataMem_NEW,它直接利用基础的C运行语言功能:

NPY_NO_EXPORT void *
PyDataMem_NEW(size_t size)
{
    void *result;

    result = malloc(size);
    ...
    return result;
}

我不确定这种设计的确切思想是什么:显然,有人希望从pymalloc的小对象优化中受益,而对于数据,这种优化将永远行不通,但随后可以使用PyMem_RawMalloc代替malloc.也许目标是能够在C例程分配的内存周围包装numpy数组并接管内存的所有权(但这在某些情况下不起作用,请参阅本文结尾处的评论).

这就解释了您正在观察的行为:对于数据(其大小根据传入的大小参数而变化)将使用PyDataMem_NEW,它绕过CPython的内存分配器,您会看到C运行时分配器的原始行为.

应该尽量避免混合使用不同的分配/取消分配例程PyArray_malloc / PyDataMem_NEW’/ mallocandPyArray_free / PyDataMem_FREE / free`:即使它适用于OS Python版本,也可能因其他组合而失败.

例如,在Windows上,当使用不同的编译器版本构建扩展时,一个可执行文件可能具有来自不同C运行时的不同内存分配器,并且malloc / free可能与不同的C内存分配器通信,这可能导致难以跟踪错误.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读