Python的垃圾回收真的这么慢?
我在使用Python开发应用时遇到了一些问题,我觉得可能和Python的垃圾回收有关,虽然我不太确定……
问题是我的应用在退出时需要花费很多时间,而且在不同功能之间切换时也很慢。
在我的应用中,我处理的是非常大的字典,里面包含成千上万的从C++类实例化的大对象。
我在程序里加了一些时间戳的输出,发现每个函数结束时,当函数内部创建的对象应该被清理时,解释器在调用下一个函数之前花了很多时间。而且在程序结束时也有同样的问题:在最后一个时间戳和出现新的命令提示符之间,程序花了很多时间(大约几个小时!)。
内存使用情况是稳定的,所以我并没有真正的内存泄漏。
有没有什么建议呢?
会不会是成千上万的大C++对象的垃圾回收太慢了?
有没有什么方法可以加快这个过程?
更新:
非常感谢大家的回答,你们给了我很多调试代码的线索 :-)
我在Scientific Linux 5上使用Python 2.6.5,这是一个基于Red Hat Enterprise 5的定制版本。实际上,我并没有使用SWIG来为我们的C++代码生成Python绑定,而是使用了Reflex/PyROOT框架。我知道这个框架在粒子物理学之外不太知名(但仍然是开源的,免费提供),我必须使用它,因为这是我们主要框架的默认选项。
在这种情况下,Python端的DEL命令并不起作用,我之前已经尝试过。DEL只会删除与C++对象关联的Python变量,而不会删除内存中的对象,因为它仍然由C++端拥有……
…我知道,这可能不太标准,有点复杂,抱歉 :-P
但根据你们的建议,我会对我的代码进行性能分析,然后再回来给你们更多细节。
附加更新:
好的,按照你们的建议,我用cProfile
对我的代码进行了分析,发现实际上gc.collect()
这个函数占用了大部分的运行时间!!
这是cProfile
和pstats
的输出结果:
>>> p.sort_stats("time").print_stats(20) Wed Oct 20 17:46:02 2010 mainProgram.profile 547303 function calls (542629 primitive calls) in 548.060 CPU seconds Ordered by: internal time List reduced from 727 to 20 due to restriction ncalls tottime percall cumtime percall filename:lineno(function) 4 345.701 86.425 345.704 86.426 {gc.collect} 1 167.115 167.115 200.946 200.946 PlotD3PD_v3.2.py:2041(PlotSamplesBranches) 28 12.817 0.458 13.345 0.477 PlotROOTUtils.py:205(SaveItems) 9900 10.425 0.001 10.426 0.001 PlotD3PD_v3.2.py:1973(HistoStyle) 6622 5.188 0.001 5.278 0.001 PlotROOTUtils.py:403(__init__) 57 0.625 0.011 0.625 0.011 {built-in method load} 103 0.625 0.006 0.792 0.008 dbutils.py:41(DeadlockWrap) 14 0.475 0.034 0.475 0.034 {method 'dump' of 'cPickle.Pickler' objects} 6622 0.453 0.000 5.908 0.001 PlotROOTUtils.py:421(CreateCanvas) 26455 0.434 0.000 0.508 0.000 /opt/root/lib/ROOT.py:215(__getattr__) [...] >>> p.sort_stats("cumulative").print_stats(20) Wed Oct 20 17:46:02 2010 mainProgram.profile 547303 function calls (542629 primitive calls) in 548.060 CPU seconds Ordered by: cumulative time List reduced from 727 to 20 due to restriction ncalls tottime percall cumtime percall filename:lineno(function) 1 0.001 0.001 548.068 548.068 PlotD3PD_v3.2.py:2492(main) 4 0.000 0.000 346.756 86.689 /usr/lib//lib/python2.5/site-packages/guppy/heapy/Use.py:171(heap) 4 0.005 0.001 346.752 86.688 /usr/lib//lib/python2.5/site-packages/guppy/heapy/View.py:344(heap) 1 0.002 0.002 346.147 346.147 PlotD3PD_v3.2.py:2537(LogAndFinalize) 4 345.701 86.425 345.704 86.426 {gc.collect} 1 167.115 167.115 200.946 200.946 PlotD3PD_v3.2.py:2041(PlotBranches) 28 12.817 0.458 13.345 0.477 PlotROOTUtils.py:205(SaveItems) 9900 10.425 0.001 10.426 0.001 PlotD3PD_v3.2.py:1973(HistoStyle) 13202 0.336 0.000 6.818 0.001 PlotROOTUtils.py:431(PlottingCanvases) 6622 0.453 0.000 5.908 0.001 /root/svn_co/rbianchi/SoftwareDevelopment [...] >>>
所以在这两个输出中,分别按“时间”和“累计时间”排序,gc.collect()
是消耗我程序运行时间最多的函数! :-P
这是内存分析工具Heapy
在返回main()
程序之前的输出。
memory usage before return: Partition of a set of 65901 objects. Total size = 4765572 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 25437 39 1452444 30 1452444 30 str 1 6622 10 900592 19 2353036 49 dict of PlotROOTUtils.Canvas 2 109 0 567016 12 2920052 61 dict of module 3 7312 11 280644 6 3200696 67 tuple 4 6622 10 238392 5 3439088 72 0xa4ab74c 5 6622 10 185416 4 3624504 76 PlotROOTUtils.Canvas 6 2024 3 137632 3 3762136 79 types.CodeType 7 263 0 129080 3 3891216 82 dict (no owner) 8 254 0 119024 2 4010240 84 dict of type 9 254 0 109728 2 4119968 86 type Index Count % Size % Cumulative % Kind (class / dict of class) 10 1917 3 107352 2 4264012 88 function 11 3647 5 102116 2 4366128 90 ROOT.MethodProxy 12 148 0 80800 2 4446928 92 dict of class 13 1109 2 39924 1 4486852 93 __builtin__.wrapper_descriptor 14 239 0 23136 0 4509988 93 list 15 87 0 22968 0 4532956 94 dict of guppy.etc.Glue.Interface 16 644 1 20608 0 4553564 94 types.BuiltinFunctionType 17 495 1 19800 0 4573364 94 __builtin__.weakref 18 23 0 11960 0 4585324 95 dict of guppy.etc.Glue.Share 19 367 1 11744 0 4597068 95 __builtin__.method_descriptor
有没有什么想法,或者如何优化垃圾回收?
我可以做更详细的检查吗?
3 个回答
如果你的问题真的是因为垃圾回收,那你可以在用完对象后,明确地使用 del()
来释放它们。
一般来说,这听起来不像是垃圾回收的问题,除非你在处理的是几TB的内存。
我同意S.Lott的看法……先对你的应用进行性能分析,然后把代码片段和结果带回来,这样我们才能提供更有效的帮助。
是的,这可能是垃圾回收的问题,但也可能是和C++代码的同步问题,或者是其他完全不同的原因(没有代码的话很难判断)。
无论如何,你应该去看看 Python/C++集成开发的特别兴趣小组,那里可以找到一些问题和加速的方法。
这是一个在Python 2.6中已知的垃圾回收问题,导致当你创建很多对象而不释放它们时,垃圾回收的速度变得很慢,就像在处理一个很大的列表一样。
有两个简单的解决办法:
第一种是,在填充大列表之前先关闭垃圾回收,填充完后再开启它。
l = [] gc.disable() for x in xrange(10**6): l.append(x) gc.enable()
第二种是,升级到Python 2.7,这个问题在这个版本中已经解决了。
我更喜欢第二种解决办法,但有时候这并不是一个可行的选择;)