2016-07-13 36 views
7

Possiedo un'utilità che genera più worker utilizzando il modulo Python multiprocessing e mi piacerebbe essere in grado di monitorare l'utilizzo della memoria tramite l'eccellente utility memory_profiler, che fa tutto ciò che voglio, in particolare campionando l'utilizzo della memoria nel tempo e tracciando il risultato finale (non mi interessa il profilo di memoria riga per riga per questa domanda).Come profilare più sottoprocessi usando Python multiprocessing e memory_profiler?

Per impostare questa domanda, ho creato una versione più semplice dello script, che ha una funzione di lavoro che alloca memoria simile a example dato nella libreria memory_profiler. Il lavoratore è la seguente:

import time 

X6 = 10 ** 6 
X7 = 10 ** 7 

def worker(num, wait, amt=X6): 
    """ 
    A function that allocates memory over time. 
    """ 
    frame = [] 

    for idx in range(num): 
     frame.extend([1] * amt) 
     time.sleep(wait) 

    del frame 

dato un carico di lavoro sequenziale di 4 lavoratori come segue:

if __name__ == '__main__': 
    worker(5, 5, X6) 
    worker(5, 2, X7) 
    worker(5, 5, X6) 
    worker(5, 2, X7) 

Esecuzione dei mprof eseguibile per il profilo my script prende 70 secondi con ogni esecuzione lavoratore uno dopo l'altro. Lo script, eseguito come segue:

$ mprof run python myscript.py 

produce il seguente grafico di utilizzo della memoria:

Sequential Memory Generating Workers

Avendo questi lavoratori vanno in parallelo con multiprocessing significa che lo script si concluderà lento come il lavoratore più lento (25 secondi). Questo script è il seguente:

import multiprocessing as mp 

if __name__ == '__main__': 
    pool = mp.Pool(processes=4) 
    tasks = [ 
     pool.apply_async(worker, args) for args in 
     [(5, 5, X6), (5, 2, X7), (5, 5, X6), (5, 2, X7)] 
    ] 

    results = [p.get() for p in tasks] 

profiler di memoria effettivamente funziona, o almeno non ci sono errori quando si utilizza mprof ma i risultati sono un po 'strano:

enter image description here

Un rapido sguardo a Activity Monitor mostra che in effetti ci sono 6 processi Python, uno per mprof uno per python myscript.py e uno per ciascun sottoprocesso di lavoro. Sembra che mprof stia misurando solo l'utilizzo della memoria per il processo python myscript.py.

Python Processes in Activity Monitor

La biblioteca memory_profiler è altamente personalizzabile, e sono abbastanza sicuro che dovrei essere in grado di catturare la memoria di ogni processo ed eventualmente scrivere per separare i file di log utilizzando la libreria stessa. Non sono sicuro di dove cominciare o come avvicinarmi a quel livello di personalizzazione.

EDIT

Dopo aver letto il copione mprof ho fatto scoprire la bandiera -C che riassume l'utilizzo della memoria di tutti i bambini (a forcella) processi. Questo porta ad un (molto migliorato) grafico come segue:

Multiprocessing Workers with Include Children Flag

Ma quello che sto cercando è l'utilizzo della memoria di ogni singolo sottoprocesso nel tempo in modo che io possa tracciare tutti i lavoratori (e il master) sullo stesso grafico. La mia idea è di avere ogni sottoprocesso memory_usage scritto in un file di registro diverso, che posso quindi visualizzare.

+0

Questa domanda è in discussione con gli sviluppatori su GitHub all'indirizzo https://github.com/fabianp/memory_profiler/issues/118 se qualcuno è interessato. – bbengfort

risposta

1

A partire da oggi, una nuova funzione è stata aggiunta alla libreria del profiler di memoria che fa esattamente questo. Se avete bisogno di questa funzionalità, prima memory_profiler aggiornamento come segue:

$ pip install -U memory_profiler 

Questo dovrebbe installare la versione v0.44 di profiler di memoria. Per verificare se questo è il caso, utilizzare il comando help sull'azione corsa:

mprof run --help 
Usage: mprof run [options] 

Options: 
    --version    show program's version number and exit 
    -h, --help   show this help message and exit 
    --python    Activates extra features when the profiling executable 
         is a Python program (currently: function 
         timestamping.) 
    --nopython   Disables extra features when the profiled executable 
         is a Python program (currently: function 
         timestamping.) 
    -T INTERVAL, --interval=INTERVAL 
         Sampling period (in seconds), defaults to 0.1 
    -C, --include-children 
         Monitors forked processes as well (sum up all process 
         memory) 
    -M, --multiprocess Monitors forked processes creating individual plots 
         for each child 

Se si vede la bandiera -M allora sei a posto!

È quindi possibile eseguire il vostro script come segue:

$ mprof run -M python myscript.py 
$ mprof plot 

e si dovrebbe ottenere una figura che assomiglia a questo:

mprof tracking individual child proccesses

Si noti che se si utilizza il flag --include-children come beh, la memoria di processo principale sarà l'utilizzo totale della memoria di tutti i bambini e principale, che è anche una trama utile.

+0

un ringraziamento speciale a @ fabian-pedregosa per aver aiutato a realizzare questo risultato! – bbengfort

+0

Che ne dici di abilitare i timestamp e il decoratore '@ profile' in questa modalità? È possibile? – petroslamb

+0

Non sono sicuro di cosa intendi per abilitare i timestamp? Penso che questo dovrebbe essere possibile con il decoratore '@ profile', usa gli stessi argomenti. – bbengfort