Сбор мусора Python может быть таким медленным?
У меня проблема с моим приложением python, и я думаю, что это связано с сборкой мусора python, даже если я не уверен...
Проблема в том, что мое приложение занимает много времени, чтобы выйти и переключиться на одну функцию на следующую.
В моем приложении я обрабатываю очень большие словари, содержащие тысячи больших объектов, которые создаются из обернутых классов С++.
Я поместил некоторые выходные данные в свою программу, и я увидел, что в конце каждой функции, когда объекты, созданные внутри функции, должны выйти из области видимости, много времени тратит интерпретатор перед вызовом следующей функции. И я наблюдаю ту же проблему в конце приложения, когда программа должна выйти: много времени (~ часов!) Проводится между последней меткой времени на экране и появлением нового приглашения.
Использование памяти стабильно, поэтому у меня действительно нет утечек памяти.
Любые предложения?
Может ли быть сборка мусора из тысяч больших объектов С++, которые замедляются?
Есть ли способ ускорить это?
UPDATE:
Большое спасибо за все ваши ответы, вы дали мне много подсказок для отладки моего кода: -)
Я использую Python 2.6.5 на Scientific Linux 5, настраиваемый дистрибутив на базе Red Hat Enterprise 5.
И на самом деле я не использую SWIG, чтобы получить привязки Python для нашего кода на С++, но фреймворк Reflex/PyROOT. Я знаю, это не очень известная внешняя физика частиц (но все еще открытый источник и свободно доступна), и я должен использовать ее, потому что это по умолчанию для нашей основной структуры.
И в этом контексте команда DEL со стороны Python не работает, я уже пробовал. DEL удаляет только переменную python, связанную с объектом С++, а не сам объект в памяти, который все еще принадлежит стороне С++...
... Я знаю, это нестандартный, я думаю, и немного сложный, извините:-P
Но следуя вашим советам, я расскажу о своем коде, и я вернусь к вам с более подробной информацией, как вы предложили.
ДОПОЛНИТЕЛЬНОЕ ОБНОВЛЕНИЕ:
Хорошо, следуя вашим предложениям, я измерил свой код с помощью cProfile
, и я обнаружил, что на самом деле функция gc.collect()
- это функция, использующая большую часть времени работы!
Здесь вывод из cProfile
+ pstats
print_stats():
>>> p.sort_stats("time").print_stats(20)
Wed Oct 20 17:46:02 2010 mainProgram.profile
547303 function calls (542629 primitive calls) in 548.060 CPU seconds
Ordered by: internal time
List reduced from 727 to 20 due to restriction
ncalls tottime percall cumtime percall filename:lineno(function)
4 345.701 86.425 345.704 86.426 {gc.collect}
1 167.115 167.115 200.946 200.946 PlotD3PD_v3.2.py:2041(PlotSamplesBranches)
28 12.817 0.458 13.345 0.477 PlotROOTUtils.py:205(SaveItems)
9900 10.425 0.001 10.426 0.001 PlotD3PD_v3.2.py:1973(HistoStyle)
6622 5.188 0.001 5.278 0.001 PlotROOTUtils.py:403(__init__)
57 0.625 0.011 0.625 0.011 {built-in method load}
103 0.625 0.006 0.792 0.008 dbutils.py:41(DeadlockWrap)
14 0.475 0.034 0.475 0.034 {method 'dump' of 'cPickle.Pickler' objects}
6622 0.453 0.000 5.908 0.001 PlotROOTUtils.py:421(CreateCanvas)
26455 0.434 0.000 0.508 0.000 /opt/root/lib/ROOT.py:215(__getattr__)
[...]
>>> p.sort_stats("cumulative").print_stats(20)
Wed Oct 20 17:46:02 2010 mainProgram.profile
547303 function calls (542629 primitive calls) in 548.060 CPU seconds
Ordered by: cumulative time
List reduced from 727 to 20 due to restriction
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.001 0.001 548.068 548.068 PlotD3PD_v3.2.py:2492(main)
4 0.000 0.000 346.756 86.689 /usr/lib//lib/python2.5/site-packages/guppy/heapy/Use.py:171(heap)
4 0.005 0.001 346.752 86.688 /usr/lib//lib/python2.5/site-packages/guppy/heapy/View.py:344(heap)
1 0.002 0.002 346.147 346.147 PlotD3PD_v3.2.py:2537(LogAndFinalize)
4 345.701 86.425 345.704 86.426 {gc.collect}
1 167.115 167.115 200.946 200.946 PlotD3PD_v3.2.py:2041(PlotBranches)
28 12.817 0.458 13.345 0.477 PlotROOTUtils.py:205(SaveItems)
9900 10.425 0.001 10.426 0.001 PlotD3PD_v3.2.py:1973(HistoStyle)
13202 0.336 0.000 6.818 0.001 PlotROOTUtils.py:431(PlottingCanvases)
6622 0.453 0.000 5.908 0.001 /root/svn_co/rbianchi/SoftwareDevelopment
[...]
>>>
Итак, на обоих выходах, отсортированных по "времени" и "кумулятивному" времени соответственно, gc.collect()
- это функция, потребляющая большую часть времени работы моей программы!:-P
И это вывод профайлера памяти Heapy
, как раз перед возвратом программы main()
.
memory usage before return:
Partition of a set of 65901 objects. Total size = 4765572 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 25437 39 1452444 30 1452444 30 str
1 6622 10 900592 19 2353036 49 dict of PlotROOTUtils.Canvas
2 109 0 567016 12 2920052 61 dict of module
3 7312 11 280644 6 3200696 67 tuple
4 6622 10 238392 5 3439088 72 0xa4ab74c
5 6622 10 185416 4 3624504 76 PlotROOTUtils.Canvas
6 2024 3 137632 3 3762136 79 types.CodeType
7 263 0 129080 3 3891216 82 dict (no owner)
8 254 0 119024 2 4010240 84 dict of type
9 254 0 109728 2 4119968 86 type
Index Count % Size % Cumulative % Kind (class / dict of class)
10 1917 3 107352 2 4264012 88 function
11 3647 5 102116 2 4366128 90 ROOT.MethodProxy
12 148 0 80800 2 4446928 92 dict of class
13 1109 2 39924 1 4486852 93 __builtin__.wrapper_descriptor
14 239 0 23136 0 4509988 93 list
15 87 0 22968 0 4532956 94 dict of guppy.etc.Glue.Interface
16 644 1 20608 0 4553564 94 types.BuiltinFunctionType
17 495 1 19800 0 4573364 94 __builtin__.weakref
18 23 0 11960 0 4585324 95 dict of guppy.etc.Glue.Share
19 367 1 11744 0 4597068 95 __builtin__.method_descriptor
Любая идея, почему, или как оптимизировать сбор мусора?
Есть ли еще более подробная проверка, которую я могу сделать?
Ответы
Ответ 1
Это известная проблема сборщика мусора в Python 2.6, вызывающая квадратичное время сбора мусора, когда многие объекты выделяются без освобождения от любого из них. население большого списка.
Существует два простых решения:
-
либо отключить сбор мусора перед заполнением больших списков и включить его впоследствии
l = []
gc.disable()
for x in xrange(10**6):
l.append(x)
gc.enable()
-
или обновите до Python 2.7, где проблема была решена
Я предпочитаю второе решение, но это не всегда вариант;)
Ответ 2
Да, это может быть сборка мусора, но также может быть некоторая синхронизация с кодом С++ или что-то совершенно другое (трудно сказать без кода).
В любом случае, вы должны взглянуть на SIG для разработки интеграции Python/С++, чтобы найти проблемы и как ускорить их.
Ответ 3
Если ваша проблема действительно является сборкой мусора, попробуйте явно освободить свои объекты, когда вы закончите с ними, используя del()
.
В общем, это не похоже на проблему сбора мусора, если мы не говорим о терабайтах памяти.
Я согласен с S.Lott... профилировать ваше приложение, а затем приносить фрагменты кода и результаты этого, и мы можем быть гораздо полезнее.