2015-08-18 11 views
5

Ho una lista enorme che devo elaborare, che richiede un po 'di tempo, quindi la divido in 4 parti e multiprocesso ogni pezzo con una qualche funzione. Ci vuole ancora un po 'di tempo per girare con 4 core, quindi ho pensato di aggiungere una barra di avanzamento alla funzione, in modo che potesse dirmi dove si trova ciascun processore durante l'elaborazione dell'elenco.Uso di click.progressbar con multiprocessing in Python

Il mio sogno era quello di avere qualcosa di simile:

erasing close atoms, cpu0 [######..............................] 13% 
erasing close atoms, cpu1 [#######.............................] 15% 
erasing close atoms, cpu2 [######..............................] 13% 
erasing close atoms, cpu3 [######..............................] 14% 

con ogni barra in movimento, come il ciclo nella funzione progredisce. Ma invece, ho un flusso continuo:

enter image description here

ecc, riempiendo la mia finestra di terminale.

Ecco lo script python principale che chiama la funzione:

from eraseCloseAtoms import * 
from readPDB import * 
import multiprocessing as mp 
from vectorCalc import * 

prot, cell = readPDB('file') 
atoms = vectorCalc(cell) 

output = mp.Queue() 

# setup mp to erase grid atoms that are too close to the protein (dmin = 2.5A) 
cpuNum = 4 
tasks = len(atoms) 
rangeSet = [tasks/cpuNum for i in range(cpuNum)] 
for i in range(tasks % cpuNum): 
    rangeSet[i] += 1 

rangeSet = np.array(rangeSet) 

processes = [] 
for c in range(cpuNum): 
    na, nb = (int(np.sum(rangeSet[:c] + 1)), int(np.sum(rangeSet[:c + 1]))) 
    processes.append(mp.Process(target=eraseCloseAtoms, args=(prot, atoms[na:nb], cell, 2.7, 2.5, output))) 

for p in processes: 
    p.start() 

results = [output.get() for p in processes] 

for p in processes: 
    p.join() 

atomsNew = results[0] + results[1] + results[2] + results[3] 

Di seguito è la funzione eraseCloseAtoms():

import numpy as np 
import click 


def eraseCloseAtoms(protein, atoms, cell, spacing=2, dmin=1.4, output=None): 
    print 'just need to erase close atoms' 

    if dmin > spacing: 
     print 'the spacing needs to be larger than dmin' 
     return 

    grid = [int(cell[0]/spacing), int(cell[1]/spacing), int(cell[2]/spacing)] 

    selected = list(atoms) 
    with click.progressbar(length=len(atoms), label='erasing close atoms') as bar: 
     for i, atom in enumerate(atoms): 
      bar.update(i) 
      erased = False 
      coord = np.array(atom[6]) 

      for ix in [-1, 0, 1]: 
       if erased: 
        break 
       for iy in [-1, 0, 1]: 
        if erased: 
         break 
        for iz in [-1, 0, 1]: 
         if erased: 
          break 
         for j in protein: 
          protCoord = np.array(protein[int(j)][6]) 
          trueDist = getMinDist(protCoord, coord, cell, vectors) 
          if trueDist <= dmin: 
           selected.remove(atom) 
           erased = True 
           break 
    if output is None: 
     return selected 
    else: 
     output.put(selected) 
+1

C'è un esempio di pronti contro termine su questo si potrebbe trovare interessante: https://github.com/aaren/multi_progress –

risposta

4

vedo due problemi nel codice.

Il primo spiega perché le barre di avanzamento mostrano spesso 100% anziché i loro reali progressi. Stai chiamando bar.update(i) che avanza i progressi della barra per passi i, quando penso che tu voglia essere aggiornato di un passo. Un approccio migliore sarebbe passare iterable alla funzione progressbar e lasciar fare l'aggiornamento automatico:

with click.progressbar(atoms, label='erasing close atoms') as bar: 
    for atom in bar: 
     erased = False 
     coord = np.array(atom[6]) 

     # ... 

Tuttavia, questo ancora non funziona con più processi iterazione contemporaneamente, ciascuna con il proprio barra di avanzamento dovuta al secondo numero con il tuo codice. Il click.progressbar documentation indica la seguente limitazione:

Nessuna stampa deve essere eseguita o la barra di avanzamento verrà distrutta involontariamente.

Ciò significa che ogni volta che una delle barre di avanzamento si aggiorna automaticamente, interromperà tutte le altre barre di avanzamento attive.

Non penso che ci sia una soluzione semplice per questo. È molto difficile aggiornare in modo interattivo un output di console su più linee (in pratica è necessario utilizzare maledizioni o una simile libreria "console GUI" con supporto dal sistema operativo). Il modulo click non ha questa capacità, può solo aggiornare la linea corrente. La tua migliore speranza probabilmente sarebbe di estendere il progetto click.progressbar di riprodurre diversi bar in colonne, come:

CPU1: [######  ] 52% CPU2: [###  ] 30% CPU3: [######## ] 84% 

Ciò richiederebbe una quantità non trascurabile di codice per farlo funzionare (soprattutto quando gli aggiornamenti sono provenienti da molteplici processi), ma non è completamente impraticabile.

2

risposta accettata dice che è impossibile con il clic e richiederebbe "quantità non trascurabile di codice per farlo funzionare".

Mentre è vero, c'è un altro modulo con questa funzionalità fuori dalla scatola: tqdm https://github.com/tqdm/tqdm che fa esattamente quello che ti serve.

Si può fare barre di avanzamento annidati in docs https://github.com/tqdm/tqdm#nested-progress-bars ecc

+0

Si TQdM ha recentemente aggiunto il supporto per parallelo barre di avanzamento come richiesto da OP come [dimostrato qui] (http://stackoverflow.com/questions/22811162/python-mulitprocessing-queue-isnt-keeping-all-the-workers-busy/37169157#37169157) e [qui] (http://stackoverflow.com/questions/3288595/multiprocessing-how-to-use-pool-map-on-a-function-defined-in-a-class/37499872#37499872), e senza usare curses né a GUI, solo caratteri di controllo standard. – gaborous