2014-04-11 13 views
21

Su un sistema a 64 bit un intero in Python richiede 24 byte. Questo è 3 volte la memoria che sarebbe necessaria in es. C per un numero intero a 64 bit. Ora, so che questo è dovuto al fatto che gli interi Python sono oggetti. Ma a cosa serve la memoria extra? Ho le mie ipotesi, ma sarebbe bello saperlo con certezza.Perché ints richiede una quantità di memoria tre volte maggiore in Python?

+2

Vedere questo articolo: http://www.laurentluce.com/posts/python-integer-objects-implementation/ e anche https://docs.python.org/2/c-api/structures.html per common strutture di oggetti in Python – DNA

+0

@DNA: che parla del tipo di base 'int' di Python 2; il tipo 'long' in Python 2 (che sostituisce il tipo' int' in Python 3) è ancora un po 'più complicato. –

+0

Si noti che questa domanda e le sue risposte sono specifiche dell'implementazione di riferimento CPython. Altre implementazioni potrebbero avere usi di memoria completamente diversi (tuttavia, si applicano gli stessi principi generali: la dimensione e gli altri metadati dell'oggetto devono essere memorizzati). – Bob

risposta

30

Ricordare che il tipo Python int non ha un intervallo limitato come C int ha; l'unico limite è la memoria disponibile.

La memoria va a memorizzare il valore, la dimensione corrente della memoria intera (la dimensione della memoria è variabile per supportare dimensioni arbitrarie) e la contabilità di oggetti standard di Python (un riferimento all'oggetto pertinente e un conteggio di riferimento).

È possibile cercare longintrepr.h source (il tipo Python 3 int era tradizionalmente conosciuto come il tipo long in Python 2); fa uso efficace della PyVarObject C type per monitorare dimensione integer:

struct _longobject { 
     PyObject_VAR_HEAD 
     digit ob_digit[1]; 
}; 

i negozi ob_digit matrice 'cifre' di 15 o 30 bit di larghezza (a seconda della piattaforma); così sul mio sistema OS X a 64 bit, un intero fino a (2^30) - 1 utilizza 1 'cifra':

>>> sys.getsizeof((1 << 30) - 1) 
28 

ma se si utilizza 2 cifre 30-bit del numero 4 byte supplementari sono necessari, ecc:

>>> sys.getsizeof(1 << 30) 
32 
>>> sys.getsizeof(1 << 60) 
36 
>>> sys.getsizeof(1 << 90) 
40 

la base 24 byte quindi sono la struttura PyObject_VAR_HEAD, tiene le dimensioni dell'oggetto, il conteggio di riferimento e il tipo puntatore (ogni 8 byte/64 bit sul mio piattaforma OS X 64-bit) .

In Python 2, interi < = sys.maxint ma> = -sys.maxint - 1 vengono memorizzate mediante un simpler structure memorizzare solo il singolo valore:

typedef struct { 
    PyObject_HEAD 
    long ob_ival; 
} PyIntObject; 

perché utilizza PyObject anziché PyVarObject non c'è ob_size campo nella struct e la dimensione della memoria è limitata a soli 24 byte; 8 per il valore long, 8 per il conteggio dei riferimenti e 8 per il puntatore dell'oggetto di tipo.

+0

Come vengono gestiti i valori negativi, se un int è dato come una sequenza di cifre? Esiste un concetto di complemento a due in python? Se stampo hex (-1) ottengo -0x1 o analogamente se stampo bin (-1) ottengo -0b1 Capisco che questo potrebbe non essere ciò che viene rappresentato internamente tuttavia come fa python a prendere la decisione che è un valore negativo se il bit più alto non è impostato? – Har

+1

@Har: la dimensione dell'oggetto è impostata su un valore negativo. vedere il [file di intestazione collegato] (https://hg.python.org/cpython/file/5e303360db14/Include/longintrepr.h#l74): * I numeri negativi sono rappresentati con ob_size <0; *. Quindi una rappresentazione intera che richiede 2 voci 'ob_digits', quindi' ob_size' è o '2' o' -2', quest'ultima che segnala che è un numero intero negativo. –

+0

quindi significa che non è un complemento a due è semplicemente un po 'nella struttura che rappresenta se è negativo o no? – Har

1

Da longintrepr.h, vediamo che un pitone 'int' oggetto è definito con questa struttura C:

struct _longobject { 
     PyObject_VAR_HEAD 
     digit ob_digit[1]; 
}; 

cifra è un valore senza segno a 32 bit. La maggior parte dello spazio è occupata dall'intestazione dell'oggetto di dimensioni variabili. Da object.h, siamo in grado di trovare la sua definizione:

typedef struct { 
    PyObject ob_base; 
    Py_ssize_t ob_size; /* Number of items in variable part */ 
} PyVarObject; 

typedef struct _object { 
    _PyObject_HEAD_EXTRA 
    Py_ssize_t ob_refcnt; 
    struct _typeobject *ob_type; 
} PyObject; 

Possiamo vedere che stiamo utilizzando un Py_ssize_t, a 64-bit assumendo sistema a 64 bit, per memorizzare il numero di "cifre" nel valore. Questo è probabilmente uno spreco. Possiamo anche vedere che l'intestazione dell'oggetto generale ha un conteggio di riferimento a 64 bit e un puntatore al tipo di oggetto, che sarà anche un 64-bit di memoria. Il conteggio dei riferimenti è necessario affinché Python sappia quando deallocare l'oggetto, e il puntatore al tipo di oggetto è necessario per sapere che abbiamo un int e non, per esempio, una stringa, poiché le strutture C non hanno modo di testare il tipo di un oggetto da un puntatore arbitrario.

_PyObject_HEAD_EXTRA non è definito su gran parte delle build di python, ma può essere utilizzato per memorizzare un elenco collegato di tutti gli oggetti Python sull'heap se la build attiva tale opzione, utilizzando altri due puntatori di 64 bit ciascuno.