Сборка мусора в Python может быть такой медленной? - PullRequest
15 голосов
/ 12 октября 2010

У меня проблема с моим приложением python, и я думаю, что оно связано с сборкой мусора python, даже если я не уверен ...

Проблема в том, что мое приложение отнимает много временивыйти и переключиться на одну функцию для следующей.

В моем приложении я работаю с очень большими словарями, содержащими тысячи больших объектов, которые создаются из обернутых классов C ++.

Я положил нескольковременные метки выводятся в моей программе, и я увидел, что в конце каждой функции, когда объекты, созданные внутри функции, должны выходить из области видимости, интерпретатор тратит много времени перед вызовом следующей функции.И я наблюдаю ту же проблему в конце приложения, когда программа должна выйти: много времени (~ часов!) Тратится между последней отметкой времени на экране и появлением новой подсказки.

Использование памяти стабильно, поэтому у меня на самом деле нет утечек памяти.

Любые предложения?

Может ли сборщик мусора из тысяч больших объектов C ++ работать медленно?

Есть ли способ ускорить это?

ОБНОВЛЕНИЕ:

Большое спасибо за все ваши ответы, вы дали мне много советов для отладки моего кода:-)

Я использую Python 2.6.5 в Scientific Linux 5, специализированный дистрибутив, основанный на Red Hat Enterprise 5. И на самом деле я не использую SWIG для получения привязок Python для нашего кода C ++, а Reflex /PyROOT рамки.Я знаю, что она не очень известна за пределами физики элементарных частиц (но все еще с открытым исходным кодом и находится в свободном доступе), и я должен использовать ее, потому что она используется по умолчанию для нашей основной инфраструктуры.

И в этом контексте команда DEL из Pythonсторона не работает, я уже пробовал.DEL только удаляет переменную python, связанную с объектом C ++, а не сам объект в памяти, который все еще принадлежит стороне C ++ ...

... Я знаю, это нестандартно, я полагаю, инемного сложно, извините: -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

Любая идея, почему или как оптимизировать мусорколлекция?

Можно ли провести более детальную проверку?

Ответы [ 3 ]

9 голосов
/ 14 июня 2014

Это известная проблема сборщика мусора в Python 2.6 , вызывающая квадратичное время для сборки мусора, когда многие объекты размещаются без освобождения любого из них, т.е. Население большого списка.
Есть два простых решения:

  1. либо отключите сборку мусора перед заполнением больших списков, а затем включите его

    l = []
    gc.disable()
    for x in xrange(10**6):
      l.append(x)
    gc.enable()
    
  2. или обновление до Python 2.7, где проблема была решена

Я предпочитаю второе решение, но это не всегда вариант;)

5 голосов
/ 12 октября 2010

Да, это может быть сборка мусора, но это также может быть некоторая синхронизация с кодом C ++ или что-то совершенно иное (трудно сказать без кода).

В любом случае, вам следует взглянуть на SIG для разработки интеграции Python / C ++ для поиска проблем и способов их ускорения.

0 голосов
/ 13 октября 2010

Если ваша проблема действительно в сборке мусора, попробуйте явно освободить ваши объекты, когда вы закончите с ними, используя del().

В общем, это не похоже на проблему сбора мусора, если только мы не говорим о терабайтах памяти.

Я согласен с S.Lott ... профилируем ваше приложение, затем возвращаем фрагменты кода и результаты этого, и мы можем быть намного полезнее.

...