我正在研究一个复杂的python代码,它在垃圾收集中花费了大约40%的执行时间。
ncalls tottime percall cumtime percall filename:lineno(function)
**6028 494.097 0.082 494.097 0.082** {built-in method gc.collect}
1900 205.709 0.108 205.709 0.108 {built-in method time.sleep}
778 26.858 0.035 383.476 0.493 func1.py:51(fill_trades)
有没有办法减少对gc.collect的调用次数?我尝试了gc.disable(),但它的有效性受到限制,因为Cpython主要使用引用计数。我正在使用python 3.6。
如果没有看到代码,这是不可能正确回答的。但是,您可以使用一些通用提示来改善这种情况。
主要是:限制分配数量。你是否经常在微小的包装中重新包装一些没用的东西?你是否经常复制部分字符串?你在做很多复制数据的消息解析吗?找出最常分配内存并改进的内容。 https://pypi.python.org/pypi/memory_profiler在这里可能会有所帮助。
针对具体情况的修复:
numpy
这样的东西会有所帮助,因为你可以使用真实的,可变的,类型化的数组而不是列表。cython
编译模块,以消除将值包装到python对象中的需要。memoryview
s保存一些分配:https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews最后 - 你确定收集时间有问题吗?从跟踪中,您可以看到列表中的第二个位置是time.sleep
。如果你的gc.collect
占运行时间的40%,那么time.sleep
需要16% - 为什么不在那时触发收集呢?无论如何,你明确地在睡觉。
编辑:此外,我相信你在某处明确地调用gc.collect
。该调用不会自动出现在pstats
输出中。要找出位置,请尝试:
your_pstats_object.print_callers('gc.collect')
我遇到了类似的问题,我的代码花了90%的时间用于垃圾收集。我的功能在测试中每个呼叫花了大约90毫秒,但在生产中每个呼叫接近1秒。我跟踪它到熊猫检查它的SettingWithCopyWarning
安静的形式。
在我的例子中,我创建了一个像df = pd.DataFrame(data)[fieldlist]
的数据帧切片,然后分配了一个新的列df['foo'] = ...
。此时df._is_copy
显示我们对原始数据帧有一个弱参数,所以当我们调用__setitem__时,它会测试_check_setitem_copy然后执行完整的垃圾收集循环以杀死weakref gc.collect(2)。
在生产中,我的代码试图每秒调用该函数几次,在缓存中有一堆大对象(dicts),因此垃圾收集周期非常昂贵。通过确保我首先没有创建副本来修复,性能提高了近15倍: - |