2014-12-29 18 views
6


sto implementando un algoritmo in Python utilizzando Biopython. Ho diversi allineamenti (serie di sequenze di uguale lunghezza) memorizzati nei file FASTA. Ogni allineamento contiene tra 500 e 30000 seq e ciascuna sequenza ha una lunghezza di circa 17000 elementi. Ogni sequenza è memorizzata come oggetto Bio.SeqRecord.SeqRecord (controllare lo SeqRecord object's API documentation per ulteriori informazioni) che contiene non solo la sequenza ma anche alcune informazioni a riguardo. L'ho letto da disco usando Bio.AlignIO.read() (controllare la AlignIO module's API documentation per maggiori informazioni), che restituisce un oggetto MultipleSeqAlignment:Cercando di parallelizzare un algoritmo di pitone con multithreading e evitare restrizioni GIL

seqs = AlignIO.read(seqs_filename, 'fasta') 
len_seqs = seqs.get_alignment_length() 
stats = {'-': [0.0] * len_seqs, 'A': [0.0] * len_seqs, 
     'G': [0.0] * len_seqs, 'C': [0.0] * len_seqs, 
     'T': [0.0] * len_seqs} 


includo questo schizzo per motivi di chiarezza: enter image description here


Perché voglio paralizzare l'analisi dell'allineamento, assegnando a ciascuna CPU disponibile un frammento di esso utilizzando lo threading module (ulteriori dettagli sul motivo per cui ho preso questa decisione in seguito):

La variabile stats è una variabile condivisa in cui memorizzo alcune informazioni sull'analisi (come si può vedere nel primo frammento di codice). Poiché ogni CPU modifica le diverse posizioni di questa variabile condivisa , penso che non sia necessario alcun controllo di accesso né alcuna primitiva di sincronizzazione. Il motivo principale per cui sto usando thread anziché processi è perché Voglio evitare di copiare l'intero oggetto MultipleSeqAlignment per ogni CPU. Ho fatto qualche ricerca e ho trovato some stackoverflow posts su questo.

Ho anche letto alcune informazioni sulla "temuta" Python Interpreter Lock Globale (GIL) (ho trovato grande informazioni sia a stackoverflow e programmers al cambio pila), ma non sono ancora sicuro al 100% se il mio algoritmo è interessato da questo. Per quanto ne so, sto caricando le sequenze in memoria una alla volta, facendo così IO su ogni iterazione. Questo è il motivo per cui penso che sia una buona idea usare i thread come indicato in this stackoverflow post che ho già menzionato prima. La struttura di base delle analisi (che ciascun thread è in esecuzione) simile a questa:

for seq in seqs: 
    num_column = start_column 
    for column in seq.seq[start_column:end_column].upper(): 
     # [...] 
      try: 
       stats[elem][num_column] = get_some_info(column) 
      except TypeError: 
       raise TypeError(('"stats" argument should be a ' 
           'dict of lists of int')) 

ho fatto alcuni test di performance che utilizzano il timeit module e the time command utilizzando gli argomenti -f "% e% M" di non controllare solo il tempo reale trascorso (in secondi) ma anche la dimensione massima residente del processo durante la sua durata (in Kbyte). Sembra che il tempo di esecuzione utilizzando i thread sia il tempo di esecuzione dell'implementazione sequenziale diviso per il numero di thread. Per la dimensione massima residente non riesco ancora a trovare un motivo.


Se avete altri suggerimenti su prestazioni o chiarezza, li apprezzerei molto.


Grazie in anticipo.

+1

Per quanto riguarda la reputazione, la cosa migliore che potevo fare era di sviare la domanda, ora ho 10+ reputazioni, suppongo, ma mi dispiace non posso aiutarti con la domanda – ZdaR

+0

Grazie @Anmol_uppal! Ho appena modificato la domanda :-) –

risposta

2

Il threading non è d'aiuto quando si desidera eseguire un algoritmo presumibilmente elevato di CPU su alcuni dati.Prova a esaminare il modulo multiprocessing, con cui ho avuto un grande successo lavorando su un progetto che eseguiva un OCR speciale in un'immagine scansionata da 100 MB.

considerare questo per risolvere il problema di memoria condivisa:

from multiprocessing import Pool 

def f(x): 
    return x*x 

if __name__ == '__main__': 
    pool = Pool(processes=4)    # start 4 worker processes 
    result = pool.apply_async(f, [10]) # evaluate "f(10)" asynchronously 
    print result.get(timeout=1)   # prints "100" unless your computer is *very* slow 
    print pool.map(f, range(10))   # prints "[0, 1, 4,..., 81]" 

Date un'occhiata a pool.imap() e pool.apply_async().

Spero che questo abbia aiutato.

+0

Si prega di spiegare perché il threading non aiuta, dal momento che uno degli usi principali del threading è consentire l'elaborazione con più di un core alla volta da un programma. –

+0

Lo è, ma in Python il threading non è realmente concorrente quando si tratta di dividere la potenza della CPU, è un bene per I/O, networking e cose simili. Librerie come 'multiprocessing' e' subprocess' esistono per un motivo – whiterock

+0

Beh, questo è vero per CPython comunque. Ed è vero per quello a causa del GIL, che l'interrogante sta cercando di aggirare. Quindi, se spostarsi su GIL è impossibile in CPython, sarebbe bello se ci fosse una spiegazione del perché. BTW, i moduli 'multiprocessing' e' subprocess' esistono anche per altri motivi, come lo snellimento di cose che erano precedentemente gestite da un insieme di funzioni dipendenti dal sistema operativo nel modulo 'os'. –