2015-08-18 9 views
5

Ich habe eine riesige Liste, die ich verarbeiten muss, die einige Zeit dauert, also teile ich es in 4 Teile und multiprocess jedes Stück mit einer Funktion. Es dauert immer noch ein wenig Zeit, um mit 4 Kernen zu laufen, also dachte ich, ich würde der Funktion einen Fortschrittsbalken hinzufügen, so dass er mir sagen könnte, wo jeder Prozessor bei der Verarbeitung der Liste ist.Verwenden von click.progressbar mit Multiprocessing in Python

war mein Traum, so etwas zu haben:

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

mit jeder Stab als Schleife bewegt sich in die Funktion fortschreitet. Aber stattdessen erhalte ich einen kontinuierlichen Fluss:

enter image description here

etc, füllen mein Terminal-Fenster.

Hier ist das Haupt Python-Skript, das die Funktion aufruft:

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] 

Im Folgenden ist die Funktion 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

Es ist ein Beispiel Repo auf das Sie interessant finden könnte: https://github.com/aaren/multi_progress –

Antwort

4

Ich sehe zwei Probleme in Ihrem Code.

Die erste erklärt, warum Ihre Fortschrittsbalken oft 100% anzeigen und nicht ihren tatsächlichen Fortschritt. Du rufst bar.update(i), was den Fortschritt der Bar um i Schritte voranbringt, wenn ich denke, dass du um einen Schritt aktualisieren willst. Ein besserer Ansatz wäre es, die iterable an die progressbar Funktion zu übergeben und lassen Sie es automatisch die Aktualisierung tun:

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

     # ... 

Dies wird jedoch noch nicht mit mehreren Prozessen arbeiten auf einmal Iterieren, jeder mit seinen eigenen Fortschrittsbalken durch zum zweiten Problem mit deinem Code. Die click.progressbar documentation gibt folgende Einschränkung an:

Kein Drucken muss geschehen oder der Fortschrittsbalken wird unbeabsichtigt zerstört.

Dies bedeutet, dass jedes Mal, wenn einer Ihrer Fortschrittsbalken sich selbst aktualisiert, alle anderen aktiven Fortschrittsbalken unterbrochen werden.

Ich glaube nicht, dass es eine einfache Lösung dafür gibt. Es ist sehr schwierig, eine mehrzeilige Konsolenausgabe interaktiv zu aktualisieren (im Grunde müssen Sie curses oder eine ähnliche "Konsolen-GUI" -Bibliothek mit Unterstützung von Ihrem Betriebssystem verwenden). Das Modul click verfügt nicht über diese Fähigkeit, es kann nur die aktuelle Zeile aktualisieren. Ihre beste Hoffnung wäre wahrscheinlich den click.progressbar Entwurf zur Ausgabe von mehreren Bars in Spalten zu erweitern, wie:

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

Diese eine nicht-triviale Menge an Code erfordern würde, um es (vor allem funktioniert, wenn der Updates kommt aus mehrer Prozesse), aber es ist nicht völlig unpraktisch.

2

akzeptierte Antwort sagt, es ist unmöglich mit einem Klick und es würde 'nicht triviale Menge an Code, um es funktionieren zu lassen'.

Obwohl es wahr ist, gibt es ein anderes Modul mit dieser Funktionalität out-of-the-box: tqdm https://github.com/tqdm/tqdm die genau das tut, was Sie brauchen.

Sie können in docs verschachtelten Fortschrittsbalken tun https://github.com/tqdm/tqdm#nested-progress-bars usw.

+0

Ja tqdm hat parallel kürzlich hinzugefügte Unterstützung Fortschrittsbalken wie von OP gefragt wie [hier demonstriert] (http://stackoverflow.com/questions/22811162/python-mulitprocessing-queue-isnt-keeping-all-the-workers-busy/37169157#37169157) und [hier] (http://stackoverflow.com/questions/3288595/multiprocessing-how-to-use-pool-map-on-a-function-defined-in-a-class/37499872#37499872), und ohne Flüche oder a GUI, nur Standardsteuerzeichen. – gaborous