2012-07-02 14 views
7

Ich versuche, die Größenunterschiede zwischen zwei verschiedenen Klassen in Python genau/definitiv zu finden. Sie sind beide neue Stilklassen, außer für einen, der keine Slots definiert hat. Ich habe zahlreiche Tests ausprobiert, um ihren Größenunterschied zu ermitteln, aber sie sind immer identisch im Speicherverbrauch.Measure Objektgröße genau in Python - Sys.GetSizeOf funktioniert nicht

Bisher habe ich sys.GetSizeOf (obj) und Heapy's Heap() -Funktion versucht, ohne positive Ergebnisse. Testcode ist unten:

import sys 
from guppy import hpy 

class test3(object): 
    def __init__(self): 
     self.one = 1 
     self.two = "two variable" 

class test4(object): 
    __slots__ = ('one', 'two') 
    def __init__(self): 
     self.one = 1 
     self.two = "two variable" 

test3_obj = test3() 
print "Sizeof test3_obj", sys.getsizeof(test3_obj) 

test4_obj = test4() 
print "Sizeof test4_obj", sys.getsizeof(test4_obj) 

arr_test3 = [] 
arr_test4 = [] 

for i in range(3000): 
    arr_test3.append(test3()) 
    arr_test4.append(test4()) 

h = hpy() 
print h.heap() 

Ausgang:

Sizeof test3_obj 32 
Sizeof test4_obj 32 

Partition of a set of 34717 objects. Total size = 2589028 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0 11896 34 765040 30 765040 30 str 
    1 3001 9 420140 16 1185180 46 dict of __main__.test3 
    2 5573 16 225240 9 1410420 54 tuple 
    3 348 1 167376 6 1577796 61 dict (no owner) 
    4 1567 5 106556 4 1684352 65 types.CodeType 
    5  68 0 105136 4 1789488 69 dict of module 
    6 183 1 97428 4 1886916 73 dict of type 
    7 3001 9 96032 4 1982948 77 __main__.test3 
    8 3001 9 96032 4 2078980 80 __main__.test4 
    9 203 1 90360 3 2169340 84 type 
<99 more rows. Type e.g. '_.more' to view.> 

Das ist alles mit Python 2.6.0. Ich versuchte auch die sizeof Methoden der Klasse außer Kraft setzen, um zu versuchen, die Größe zu bestimmen, indem die einzelnen sizeofs Summieren aber, dass keine unterschiedlichen Ergebnisse brachte:

class test4(object): 
    __slots__ = ('one', 'two') 
    def __init__(self): 
     self.one = 1 
     self.two = "two variable" 
    def __sizeof__(self): 
     return super(test4, self).__sizeof__() + self.one.__sizeof__() + self.two.__sizeof__() 

Ergebnisse mit dem sizeof Methode überschrieben:

Sizeof test3_obj 80 
Sizeof test4_obj 80 

Antwort

4

sys.getsizeof gibt eine Zahl, die speziellere und weniger nützlich ist, als die Leute denken. Wenn Sie die Anzahl der Attribute auf sechs erhöhen, bleibt Ihr test3_obj tatsächlich bei 32, aber test4_obj springt auf 48 Bytes. Dies liegt daran, dass getsizeof die Größe der PyObject-Struktur zurückgibt, die den Typ implementiert, der für test3_obj nicht das dict enthält, das die Attribute enthält, aber für test4_obj werden die Attribute nicht in einem dict gespeichert, sie werden in Slots gespeichert Sie sind in der Größe berücksichtigt.

Aber eine Klasse definiert mit __slots__ nimmt weniger Speicher als eine Klasse ohne, gerade weil es kein Diktat gibt, um die Attribute zu halten.

Warum übersteuern __sizeof__? Was versuchst du wirklich zu erreichen?

+0

Die Größe von override war zu sehen, ob vielleicht die eingebaute Größe der Methode nicht korrekt die Größe der Variablen gemessen wurde. –

+0

Was würden Sie vorschlagen, ist der beste Weg, um die Größenunterschiede zwischen solchen einfachen Objekten zu bestimmen? –

+0

Das hängt davon ab, warum Sie die Größe wissen möchten. Welches Problem versuchen Sie zu lösen? –

0

Überprüfen Sie zuerst die Größe des Pyton-Prozesses im Speichermanager Ihres os ohne viele Objekte.

Zweitens machen viele Objekte einer Art und überprüfen Sie die Größe erneut.

Drittens machen viele Objekte der anderen Art und überprüfen Sie die Größe.

Wiederholen Sie dies ein paar Mal und wenn die Größen der einzelnen Schritte gleich bleiben, haben Sie etwas Vergleichbares.

+0

Ich bin neugierig, was für eine Genauigkeit das für mich bedeuten würde? Außerdem ... Ich würde einen effizienten Weg brauchen, um dies mehrere Male auszuführen und dann alles zu mitteln. –

0

Sie könnten eine andere Implementierung für das Erhalten der Größe der Objekte im Speicher verwenden möchten:

>>> import sys, array 
>>> sizeof = lambda obj: sum(map(sys.getsizeof, explore(obj, set()))) 
>>> def explore(obj, memo): 
    loc = id(obj) 
    if loc not in memo: 
     memo.add(loc) 
     yield obj 
     if isinstance(obj, memoryview): 
      yield from explore(obj.obj, memo) 
     elif not isinstance(obj, (range, str, bytes, bytearray, array.array)): 
      # Handle instances with slots. 
      try: 
       slots = obj.__slots__ 
      except AttributeError: 
       pass 
      else: 
       for name in slots: 
        try: 
         attr = getattr(obj, name) 
        except AttributeError: 
         pass 
        else: 
         yield from explore(attr, memo) 
      # Handle instances with dict. 
      try: 
       attrs = obj.__dict__ 
      except AttributeError: 
       pass 
      else: 
       yield from explore(attrs, memo) 
      # Handle dicts or iterables. 
      for name in 'keys', 'values', '__iter__': 
       try: 
        attr = getattr(obj, name) 
       except AttributeError: 
        pass 
       else: 
        for item in attr(): 
         yield from explore(item, memo) 


>>> class Test1: 
    def __init__(self): 
     self.one = 1 
     self.two = 'two variable' 


>>> class Test2: 
    __slots__ = 'one', 'two' 
    def __init__(self): 
     self.one = 1 
     self.two = 'two variable' 


>>> print('sizeof(Test1()) ==', sizeof(Test1())) 
sizeof(Test1()) == 361 
>>> print('sizeof(Test2()) ==', sizeof(Test2())) 
sizeof(Test2()) == 145 
>>> array_test1, array_test2 = [], [] 
>>> for _ in range(3000): 
    array_test1.append(Test1()) 
    array_test2.append(Test2()) 


>>> print('sizeof(array_test1) ==', sizeof(array_test1)) 
sizeof(array_test1) == 530929 
>>> print('sizeof(array_test2) ==', sizeof(array_test2)) 
sizeof(array_test2) == 194825 
>>> 

einfach sicher, dass Sie an diesem Code keine unendliche Iteratoren geben, wenn Sie wieder eine Antwort wollen.

+0

"Yield from" Ist das nicht python3-spezifische Syntax? –

+0

Ja, wenn der Rest des Codes durch '2to3.py' läuft. Die Portierung dorthin, wo 'yield from' nicht verfügbar ist, sollte ziemlich einfach sein. –

+0

str sollte nicht iteriert werden, um ihre Einzeichner-Teilstrings zu überprüfen, schlug ich eine Änderung vor, die dies berücksichtigt. – Adirio

0

Ich stieß auf ein ähnliches Problem und schrieb meinen eigenen Helfer, um die Drecksarbeit zu erledigen. Überprüfen Sie es here