[内存管理之with-open] With-Open打开体数据文件时的内存管理机制探究

本文由Markdown语法编辑器编辑完成。

1. 背景:

近期在调研一个问题,就是with…open这个函数,在大量的请求几乎同时到达时,对于几百兆的体数据文件(如nii.gz, mha)等的内存加载方式是怎么样的。

之前模糊地的认为,既然已经用with open将文件打开了,那是不是python就会将这个文件的整体内容,都加载到内存中呢。要不然,怎么实现,读取文件中的内容呢。

但是现实中通过监测容器的性能,又觉得不是这样子的。因为,如果前端突然过来几百个请求,而每个请求,其实只需要读取这个大文件中的一小部分。那么如果每个请求过来,都将这个大文件(比如几百兆)加载到内存中的话,那内存岂不是瞬间就被撑爆了。
而实际上,我的服务,并没有因为这几百,甚至上千的请求,而内存暴涨。

所以,一定是有什么机制,来处理这样的情况。

首先,咨询了一下gpt, 看它对于这种情况,是怎么理解的。

KIMI给出的解答:
在这里插入图片描述

GPT-4o给出的解答:
在这里插入图片描述

根据以上两款gpt给出的回答,可以确定with…open, 仅仅是获得了相应文件的文件指针,并没有真正的,立刻将文件全部加载到内存中。只有调用f.read(), 里面给予了要读取的文件内容的大小,才会将这部分内容,加载到内存中。

为了更加形象地展示这个过程,我借助于python中常用的监测内存变化的工具——memory_profile来做测试。

关于memory_profile的用法,可以参考它的介绍:
memory_profiler 介绍

2. 基于memory_profile, 展示with…open的内存使用过程

以下演示代码,主要完成,通过with…open, 读取一个几百兆的.mha文件, 并且通过f.read()读取不同大小的内容,查看这个过程中的内存变化过程。

2.1 创建py文件,利用文件指针读取文件

如图,创建一个test_mem.py的文件。
里面定义一个my_func()的函数,主要用来通过with open打开一个本地的.mha文件,并尝试通过文件指针的方式,读取该.mha文件中的一部分数据.

import os                                                                                                                                                                                      
import numpy as np

mha_path = "/path/to/xxx.mha"

@profile
def my_func():
    with open(mha_path, "rb") as f:
        last = f.seek(0, os.SEEK_END)
        print(f"last is: {last}")
        sdb = 512 * 512 * 2 
        f.seek(0)
        slice_array = np.frombuffer(f.read(sdb), dtype=np.uint16)

if __name__ == '__main__':
    my_func()

2.2 利用pip安装memory_profiler, 监控内存使用

由于函数my_func()加上了@profile的装饰器。因此,可以通过运行py文件的方式,查看该函数每一行的内存占用,截图如下:

python -m memory_profiler test_mem.py

在这里插入图片描述
首先可以看到,通过f.seek(0, os.SEEK_END), 可以将文件指针直接移动到该文件的末尾,返回的大小: 258998667, 也就是这个文件在内存中占据的大小。
258998667B == 247.00MB, 基本和在linux上,看到当前的.mha文件248MB, 大小近似。

而该函数中,真正引起内存变化的,是最后一句:

# 从文件头开始,读取长度为: 512*512*2B大小的内存,转化为np.uint16的numpy_array, 返回。
slice_array = np.frombuffer(f.read(sdb), dtype=np.uint16)

而从memory_profiler给出的逐行内存视图中,也可以看到,在执行最后一行后,这个程序占据的内存,增加了0.500MB, 也就是 5125122 == 524288 B = 0.5 MB.
也就是前面的那些代码,都没有多余的内存分配。只有最后一行,真正的使用文件指针 f.read()的时候,才需要额外分配相应的内存。

我们将最后一行, f.read()里面的大小,乘以10倍后,再查看一下内存的分配。

slice_array = np.frombuffer(f.read(sdb * 10), dtype=np.uint16)

再次运行指令:

python -m memory_profiler test_mem.py

可以看到内存的监测变化:
在这里插入图片描述

通过以上的简单测试,我们了解了在使用with…open打开一个大文件时,并不会造成内存的额外分配。只有真正地调用f.read()函数时,才会根据用户需要读取的内存块的大小,来分配相应的内存, 而不会造成资源的额外浪费。

完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inter_peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值