我有一个应用程序,该应用程序需要使用Python(3.5.2)初始化大量对象,并偶尔遇到运行缓慢的情况。
这种减速似乎发生在特定的初始化上:对__init__
的大多数调用持续时间不到1 ns,但是其中一些调用有时可持续数十秒。
我已经能够使用下面的代码片段(可初始化500k一个简单对象)重现此内容。
import cProfile
class A:
def __init__(self):
pass
cProfile.run('[A() for _ in range(500000)]')
我正在笔记本中运行此代码。在大多数情况下(9/10),此代码会输出以下内容(正常执行)
500004 function calls in 0.675 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
500000 0.031 0.000 0.031 0.000 <ipython-input-5-634b77609653>:2(__init__)
1 0.627 0.627 0.657 0.657 <string>:1(<listcomp>)
1 0.018 0.018 0.675 0.675 <string>:1(<module>)
1 0.000 0.000 0.675 0.675 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
其他时候,它输出以下内容(执行缓慢)
500004 function calls in 40.154 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
500000 0.031 0.000 0.031 0.000 <ipython-input-74-634b77609653>:2(__init__)
1 40.110 40.110 40.140 40.140 <string>:1(<listcomp>)
1 0.014 0.014 40.154 40.154 <string>:1(<module>)
1 0.000 0.000 40.154 40.154 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
使用tqdm,循环似乎陷入了一次迭代。重要的是要注意,我能够在已分配大量内存的笔记本中重现此内容。
我怀疑它来自对垃圾收集器使用的对象的引用列表,这些引用可能需要不时复制。
这里到底发生了什么,有什么办法可以避免这种情况?