【问题标题】:Python pandas spending excessive time in garbage collectionPython pandas 在垃圾收集上花费过多时间
【发布时间】:2018-02-26 22:43:46
【问题描述】:

我正在编写一段复杂的 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。

【问题讨论】:

  • 你能发布一些代码吗?很难说为什么您的代码会如此频繁地触发垃圾收集而没有看到它,或者一个与您的真实代码足够相似的可重现示例。
  • 我没有手动调用 gc.collect。这是 pstats 的输出。代码太大,不能在这里发布。代码严重依赖对象。
  • 如果有人能够在没有看到您的任何代码的情况下回答这个问题,我会感到惊讶。带有minimal reproducible example 的问题更有可能得到答案。尤其是像这样复杂的东西。
  • “严重依赖对象”。这就是垃圾收集器如此努力工作的原因

标签: python pandas garbage-collection performance-testing


【解决方案1】:

我遇到了类似的问题,我的代码将 90% 的时间用于垃圾收集。我的函数在测试中每次调用大约需要 90 毫秒,但在生产中每次调用接近 1 秒。我将它追踪到 pandas 检查其 SettingWithCopyWarning 的安静形式。

在我的例子中,我创建了一个像 df = pd.DataFrame(data)[fieldlist] 这样的数据框切片,然后分配了一个新列 df['foo'] = ...。此时df._is_copy 表明我们对原始数据帧有一个弱引用,因此当我们调用__setitem__ 时,它会测试_check_setitem_copy,然后它会执行完整的垃圾回收周期来杀死弱引用gc.collect(2)

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

【讨论】:

    【解决方案2】:

    如果不看代码,这实际上是不可能正确回答的。不过,您可以使用一些通用技巧来改善这种情况。

    主要的是:限制分配的数量。您是否经常在无用的小包装器中重新包装一些值?您是否经常复制部分字符串?您是否在进行大量复制数据的消息解析?找出最常分配内存的内容并加以改进。 https://pypi.python.org/pypi/memory_profiler 在这里可能会有所帮助。

    针对特定情况的修复:

    • 您是否正在执行大量数学密集型运算?也许改用 numpy 之类的东西会有所帮助,因为您可以使用真实的、可变的、类型化的数组而不是列表。
    • 你有很多数据处理代码吗?您可以在其上注释类型并使用 cython 编译模块,从而无需将值包装到 python 对象中。
    • 对于原始内存(解析/文件处理/...),您可以使用memoryviews 节省一些分配: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')
    

    【讨论】:

    • 感谢您的建议。
    • 感谢您的建议。 gc.collect() 被 pandas 调用。经过一番搜索,我发现 pd.set_option('mode.chained_assignment', None) 可以禁用 pd.set_option('mode.chained_assignment', None) 中对 pds 中 gc.collect 的显式调用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2012-06-28
    • 1970-01-01
    • 2011-09-01
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多