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 对象中的需要。
- 对于原始内存(解析/文件处理/...),您可以使用
memoryview
s 保存一些分配: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 倍:-|
我正在处理一段复杂的 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 对象中的需要。 - 对于原始内存(解析/文件处理/...),您可以使用
memoryview
s 保存一些分配: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 倍:-|