我写了一个Python程序,它作用于一个大的输入文件,以创建数百万个表示三角形的对象。算法为:
读取输入文件 处理该文件并创建一个三角形列表,由它们的顶点表示 以OFF格式输出顶点:顶点列表后面跟着三角形列表。三角形由顶点列表中的索引表示
OFF要求我在打印三角形之前打印出完整的顶点列表,这意味着在我将输出写入文件之前,我必须将三角形列表保存在内存中。与此同时,由于列表的大小,我得到了内存错误。
告诉Python我不再需要某些数据,并且可以释放这些数据的最佳方式是什么?
我写了一个Python程序,它作用于一个大的输入文件,以创建数百万个表示三角形的对象。算法为:
读取输入文件 处理该文件并创建一个三角形列表,由它们的顶点表示 以OFF格式输出顶点:顶点列表后面跟着三角形列表。三角形由顶点列表中的索引表示
OFF要求我在打印三角形之前打印出完整的顶点列表,这意味着在我将输出写入文件之前,我必须将三角形列表保存在内存中。与此同时,由于列表的大小,我得到了内存错误。
告诉Python我不再需要某些数据,并且可以释放这些数据的最佳方式是什么?
当前回答
不幸的是(取决于你的Python版本和发行版),某些类型的对象使用“自由列表”,这是一种简洁的局部优化,但可能会导致内存碎片,特别是通过将越来越多的内存“指定”给特定类型的对象,从而无法用于“普通基金”。
The only really reliable way to ensure that a large but temporary use of memory DOES return all resources to the system when it's done, is to have that use happen in a subprocess, which does the memory-hungry work then terminates. Under such conditions, the operating system WILL do its job, and gladly recycle all the resources the subprocess may have gobbled up. Fortunately, the multiprocessing module makes this kind of operation (which used to be rather a pain) not too bad in modern versions of Python.
在您的用例中,似乎子进程积累一些结果并确保这些结果对主进程可用的最佳方法是使用半临时文件(我所说的半临时是指,不是那种关闭时自动消失的文件,而是在您全部使用完后显式删除的普通文件)。
其他回答
I had a similar problem in reading a graph from a file. The processing included the computation of a 200 000x200 000 float matrix (one line at a time) that did not fit into memory. Trying to free the memory between computations using gc.collect() fixed the memory-related aspect of the problem but it resulted in performance issues: I don't know why but even though the amount of used memory remained constant, each new call to gc.collect() took some more time than the previous one. So quite quickly the garbage collecting took most of the computation time.
为了解决内存和性能问题,我改用了我曾经在某个地方读过的多线程技巧(对不起,我再也找不到相关的帖子了)。之前,我在一个大的for循环中读取文件的每一行,处理它,并每隔一段时间运行gc.collect()来释放内存空间。现在我调用一个函数,在一个新线程中读取和处理文件的一个块。一旦线程结束,内存就会自动释放,而不会出现奇怪的性能问题。
实际上它是这样工作的:
from dask import delayed # this module wraps the multithreading
def f(storage, index, chunk_size): # the processing function
# read the chunk of size chunk_size starting at index in the file
# process it using data in storage if needed
# append data needed for further computations to storage
return storage
partial_result = delayed([]) # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100 # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
# we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
partial_result = delayed(f)(partial_result, index, chunk_size)
# no computations are done yet !
# dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
# passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
# it also allows you to use the results of the processing of the previous chunks in the file if needed
# this launches all the computations
result = partial_result.compute()
# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided
其他人已经发布了一些方法,您可以“哄骗”Python解释器释放内存(或以其他方式避免内存问题)。你应该先尝试他们的想法。但是,我觉得有必要直接回答你的问题。
实际上没有任何方法可以直接告诉Python释放内存。事实是,如果你想要这么低的控制级别,你将不得不用C或c++写一个扩展。
也就是说,有一些工具可以帮助解决这个问题:
cython 痛饮 提高python
Python是垃圾回收的,所以如果你减少列表的大小,它会回收内存。你也可以使用"del"语句完全摆脱一个变量:
biglist = [blah,blah,blah]
#...
del biglist
根据Python官方文档,你可以显式调用gc.collect()来释放未引用的内存。例子:
import gc
gc.collect()
你应该在使用del标记你想丢弃的东西后这样做:
del my_array
del my_object
gc.collect()
(del可以成为你的朋友,因为当没有其他引用时,它将对象标记为可删除。现在,CPython解释器通常会保留这个内存以供以后使用,所以您的操作系统可能看不到“释放”的内存。)
通过对数据使用更紧凑的结构,也许一开始就不会遇到内存问题。 因此,与标准数组模块或第三方numpy模块使用的格式相比,数字列表的内存效率要低得多。您可以通过将顶点放在NumPy 3xN数组中,将三角形放在n元素数组中来节省内存。