A Dimarts, 11 d'agost de 2009, Phil Thompson va escriure:
> On Tue, 11 Aug 2009 21:10:58 +0200, Albert Cervera i Areny
>
> <[email protected]> wrote:
> > A Dimarts, 11 d'agost de 2009, Phil Thompson va escriure:
> >> On Sun, 2 Aug 2009 02:12:52 +0200, Albert Cervera i Areny
> >>
> >> <[email protected]> wrote:
> >> > I've been having problems with my application consuming too much
>
> memory
>
> >> > after
> >> > some time running and today decided to take a deeper look. I've ended
> >> > up
> >> > with
> >> > the attach test.py script which either demonstrates PyQt is not
>
> freeing
>
> >> > memory
> >> > appropiately when signals are involved or I simply don't understand
>
> how
>
> >> > this
> >> > works.
> >> >
> >> > As you can see the script creates lists with 100.000 QObjects and
> >> > prints
> >> > the
> >> > memory used. As python won't free memory but reuse what has already
> >> > been
> >> > freed
> >> > I expect a call like:
> >> >
> >> > list = []
> >> > fill in the list with lots of data
> >> >
> >> > to take as much memory as:
> >> >
> >> > list = []
> >> > fill in the list with lots of data
> >> > list = []
> >> > fill in the list with the same lots of data
> >> >
> >> > If you give it a try, you'll realize that this is true if you create
> >> > 100.000
> >> > objects with no signal connections. But if you connect and disconnect
> >> > a
> >> > signal for those objects, the memory used after the second fill is
> >> > larger
> >> > than
> >> > after the first one.
> >> >
> >> > It seems to me that some data is being leaked in connect() and
> >>
> >> disconnect()
> >>
> >> > functions (which, by the way, take up a lot of memory).
> >> >
> >> > Here's the output of the script in my system:
> >> >
> >> > $ python test.py one no
> >> > Executing 'one' without signals
> >> > Memory:  21564
> >> >
> >> > $ python test.py one yes
> >> > Executing 'one' with signals
> >> > Memory:  64992
> >> >
> >> > $ python test.py two yes
> >> > Executing 'two' with signals
> >> > Memory:  125592
> >> >
> >> > $ python test.py two+remove yes
> >> > Executing 'two+remove' with signals
> >> > Memory:  93880
> >> >
> >> > $ python test.py three+remove yes
> >> > Executing 'three+remove' with signals
> >> > Memory:  122808
> >> >
> >> > So "two+remove yes" should be using 64992 Kb (just like "one yes") but
> >> > it
> >> > uses
> >> > 50% more. The same happens with "three+remove yes", again more 30 Mb!
> >>
> >> The proxies that are created to allow Python callables to be used as Qt
> >> slots are destroyed using QObject::deleteLater(). As your example
>
> doesn't
>
> >> have an event loop they never get destroyed.
> >>
> >> In tonight's snapshot I've changed the implementation so they get
> >> destroyed
> >> immediately as it does simplify the code a little.
> >
> > Thank you very much. By the way, is there a reason why creating 100.000
> > objects uses only 21564 Kb and connecting them to a slot uses 64992 Kb.
>
> It
>
> > seems a lot of memory for "just" connecting signals...
>
> It's not to a slot, it's to a Python callable that needs a proxy to make it
> look like a slot. The proxy is bigger than the original object (as it does
> more).
>
> You can prevent the creation of a proxy by making sure the "slot" is a real
> Qt slot by using the @pyqtSlot decorator.

Thanks again Phil. I modified the test application and run several tests to 
compare how the several options affect to memory usage and performance. Here 
are the results in my computer.

                    MEMORY     TIME
Without signals     21572Kb    0.96s 
With slots          32576Kb    4.07s
Python Callable     64988Kb    5.25s

Maybe it'd be good to add to the reference guide that using decorators is both 
faster and more memory efficient.

>
> Phil


-- 
Albert Cervera i Areny
http://www.NaN-tic.com
Mòbil: +34 669 40 40 18
import os
import sys
from PyQt4.QtCore import *

def quit():
	print "Memory: ", int(os.popen('ps -p %d -o rss | tail -1' % os.getpid()).read())
	sys.exit()

class Test(QObject):
	@pyqtSlot()
	def slot(self):
		print "SLOT"

	def init(self, l):
		for x in xrange(100000):
			o = QObject()
			if signals:
				if sigtype == 'func':
					o.connect( o, SIGNAL('destroyed(QObject*)'), self.slot )
					o.disconnect( o, SIGNAL('destroyed(QObject*)'), self.slot )
				else:
					o.connect( o, SIGNAL('destroyed(QObject*)'), self, SLOT('slot()') )
					o.disconnect( o, SIGNAL('destroyed(QObject*)'), self, SLOT('slot()') )
			l.append( o )

if len(sys.argv) <= 3:
	print "Syntax: test.py one|two|two+remove|three+remove yes|no func|deco"
	quit()
type=sys.argv[1]
signals=sys.argv[2]
sigtype=sys.argv[3]
if signals == 'yes':
	signals = True
	print "Executing '%s' with signals" % type
else:
	signals = False
	print "Executing '%s' without signals" % type


if type == 'one':
	t = Test()
	l1 = []
	t.init(l1)
	quit()

if type == 'two':
	t = Test()
	l1 = []
	t.init(l1)
	l2 = []
	t.init(l2)
	quit()

if type == 'two+remove':
	t = Test()
	l1 = []
	t.init(l1)
	l1 = [] # The first list is removed before executing again
	l2 = []
	t.init(l2)
	quit()

if type == 'three+remove':
	t = Test()
	l1 = []
	t.init(l1)
	l1 = [] # The first list is removed before executing again
	l2 = []
	t.init(l2)
	l2 = [] # The first list is removed before executing again
	l3 = []
	t.init(l3)
	quit()

_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to