2011-01-27 10 views

risposta

5

MODIFICA: correzioni PyMem_Malloc e PyObject_Malloc misti; sono due chiamate diverse.

Senza il PYMALLOC_DEBUG macro attivato, PyMem_Malloc è un alias di libc di malloc(), avente un caso speciale: chiamata PyMem_Malloc allocare byte zero restituirà un puntatore non NULL, mentre malloc (zero_bytes) potrebbero restituire un valore NULL o sollevare errore di sistema (source code reference):

/* malloc. Notare che nbytes == 0 tenta di restituire un puntatore non NULL, distinto * da tutti gli altri puntatori attualmente attivi. Questo potrebbe non essere possibile. */

Inoltre, v'è una nota consultivo per la pymem.h header file:

Non mischiare mai chiamate a PyMem_ con chiamate verso la piattaforma di malloc/realloc/ calloc/libero.Ad esempio, su Windows DLL diverse possono finire con l'uso di diversi heap e se si utilizza PyMem_Malloc si ottiene la memoria dall'heap utilizzato dalla DLL Python ; potrebbe essere un disastro se si scarica direttamente ( ) direttamente nella propria estensione . L'utilizzo di PyMem_Free invece di assicura che Python possa restituire la memoria nell'heap corretto. Come altro esempio, in modalità PYMALLOC_DEBUG, Python avvolge tutte le chiamate a tutte le funzioni di memoria PyMem_ e PyObject_ in involucri speciali di debug che aggiungono informazioni aggiuntive di debug per blocchi di memoria dinamica. Le routine del sistema non hanno idea di cosa fare con quella roba, e i wrapper Python non hanno idea di cosa fare con blocchi grezzi ottenuti direttamente da quindi le routine di sistema.

Poi, ci sono alcune accordature specifiche Python all'interno PyMem_Malloc PyObject_Malloc, una funzione utilizzata non solo per estensioni C, ma per tutte le allocazioni dinamiche durante l'esecuzione di un programma Python, come 100*234, str(100) o 10 + 4j:

>>> id(10 + 4j) 
139721697591440 
>>> id(10 + 4j) 
139721697591504 
>>> id(10 + 4j) 
139721697591440 

Le precedenti istanze complex() sono piccoli oggetti allocati su un pool dedicato.

piccoli oggetti (< 256 bytes) assegnazione con PyMem_Malloc PyObject_Malloc è molto efficiente poiché è fatto da un pool di 8 byte blocchi allineati, una piscina per ciascuna dimensione del blocco esistente. Ci sono anche blocchi di Pages e Arenas per allocazioni maggiori.

Questo commento sul source code spiega come la chiamata PyObject_Malloc è ottimizzato:

/* 
* The basic blocks are ordered by decreasing execution frequency, 
* which minimizes the number of jumps in the most common cases, 
* improves branching prediction and instruction scheduling (small 
* block allocations typically result in a couple of instructions). 
* Unless the optimizer reorders everything, being too smart... 
*/ 

piscine, Pages e Arenas sono ottimizzazioni volte a ridurre external memory fragmentation dei programmi Python esecuzione prolungata.

Verificare the source code per la documentazione dettagliata completa sugli interni di memoria di Python.

+0

Gli allocatori nativi sono già estremamente ottimizzati. Questo non ha alcuna influenza sui moduli di estensione - se non altro, è solo un ulteriore sovraccarico per rallentare le cose. –

+0

@Glenn Hai numeri difficili da sostenere? I documenti sono abbastanza estesi e dettagliati per essere solo un "overhead per rallentare le cose". – vz0

+0

Ho esperienza e buon senso: l'allocatore di sistema influenza la velocità dell'intero sistema, quindi è necessario un grande sforzo per ottimizzarli. Se stai sostenendo che Python migliora su questo - per l'uso del modulo di estensione, non solo il caso speciale del core Python di basso livello - questa è la tua richiesta di backup. –

1

Dalla mia esperienza di scrittura di funzioni MATLAB .mex, penso che il fattore determinante più grande per l'utilizzo di malloc o meno sia la portabilità. Supponiamo che tu abbia un file di intestazione che esegue un carico di funzioni utili usando solo i tipi di dati interni (non c'è bisogno dell'interazione dell'oggetto Python, quindi nessun problema con l'uso di malloc) e all'improvviso ti accorgi di voler portare quel file di intestazione su un diverso codice che ha niente a che fare con Python (forse è un progetto scritto esclusivamente in C), usare malloc sarebbe ovviamente una soluzione molto più portabile.

Ma per il tuo codice che è puramente un'estensione Python, la mia reazione iniziale sarebbe di aspettarsi che la funzione nativa c funzioni più velocemente. Non ho prove per confermare questo :)

+2

È normale aspettarsi che la funzione C nativa migliori. Il fatto è che sono entrambe funzioni native C. :-) –

+1

Beh, immagino che siano ... bel tecnicismo :) Immagino di voler dire di più che mi aspetterei che il sistema di allocazione di memoria nativa di C sia più veloce di C nativo implementato per il sistema di allocazione di memoria di Python: P – William

+2

Ho usato sia malloc che PyMem_Malloc. I miei benchmark hanno mostrato una differenza di prestazioni trascurabile tra i due. PyMem_Malloc potrebbe essere stato più veloce, ma non credo che la differenza fosse statisticamente rilevante. YMMV, ovviamente. – casevh

6

È perfettamente OK per le estensioni per allocare memoria con malloc o altri allocatori di sistema. Questo è normale e inevitabile per molti tipi di moduli: la maggior parte dei moduli che avvolgono altre librerie, che a loro volta non sanno nulla di Python, causerà allocazioni native quando si verificano all'interno di quella libreria. (Alcune librerie ti consentono di controllare l'allocazione in modo sufficiente per impedirlo, la maggior parte no.)

C'è un serio svantaggio nell'usare PyMem_Malloc: devi tenere il GIL quando lo usi. Le librerie native spesso desiderano rilasciare GIL quando eseguono calcoli intensivi della CPU o eseguono chiamate che potrebbero bloccare, come I/O. La necessità di bloccare il GIL prima delle allocazioni può essere da qualche parte tra un inconveniente e un problema di prestazioni.

L'utilizzo dei wrapper di Python per l'allocazione della memoria consente di utilizzare il codice di debug della memoria di Python. Con strumenti come Valgrind, tuttavia, dubito del valore del mondo reale.

Avrete bisogno di usare queste funzioni se l'API lo richiede; ad esempio, se viene passata un'API a un puntatore che deve essere allocato con queste funzioni, quindi può essere liberato con esse. Escludendo un motivo esplicito come quello per usarli, rimango con allocazione normale.

Problemi correlati