Python pandas 在垃圾回收上花费过多时间

Python pandas spending excessive time in garbage collection

我正在处理一段复杂的 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 在这里可能会有帮助。

Situation-specific 修复:

  • 您是否进行了很多 math-intensive 操作?也许转向 numpy 之类的东西会有所帮助,因为您可以使用真实的、可变的、类型化的数组而不是列表。
  • 你有很多数据处理代码吗?您可以在其上注释类型并使用 cython 编译模块以消除将值包装到 python 对象中的需要。
  • 对于原始内存(解析/文件处理/...),您可以使用 memoryviews 保存一些分配:https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews

最后 - 您确定收集时间有问题吗?从trace可以看出,排在第二位的是time.sleep。如果您的 gc.collect 占用 40% 的运行时间,那么 time.sleep 占用 16% - 为什么不在那个时候触发收集呢?无论如何,你显然在睡觉。

编辑:此外,我相信您在某处明确调用 gc.collect。该调用不会自动出现在 pstats 输出中。要找出位置,请尝试:

your_pstats_object.print_callers('gc.collect')

我 运行 遇到了类似的问题,我的代码 90% 的时间都花在了垃圾回收上。我的函数在测试中每次调用大约花费 90 毫秒,但在生产中每次调用接近 1 秒。我追踪到 pandas 检查其 SettingWithCopyWarning 的安静形式。

在我的例子中,我创建了一个像 df = pd.DataFrame(data)[fieldlist] 这样的数据框切片,然后分配了一个新列 df['foo'] = ...。此时 df._is_copy 表明我们对原始数据框有一个弱引用,所以当我们调用 __setitem__ it tests _check_setitem_copy which then does a full garbage collection cycle to kill of the weakref gc.collect(2).

在生产中,我的代码试图每秒调用该函数几次,缓存中有一堆大对象 (dict),因此垃圾收集周期非常昂贵。通过确保我没有首先创建副本来修复,并且性能提高了近 15 倍:-|