Я не знаю, как распараллелить код в Python, который берет каждую строку файла FASTA и создает некоторую статистику, например, вычисляет содержимое GC. У вас есть несколько советов или библиотек, которые помогут мне сократить время, затрачиваемое на выполнение?
Я пытался использовать os.fork(), но это дает мне больше времени выполнения, чем последовательный код. Вероятно, из-за того, что я не очень хорошо знаю, как дать каждому ребенку различную последовательность.
#Computing GC Content
from Bio import SeqIO
with open('chr1.fa', 'r') as f:
records = list (SeqIO.parse(f,'fasta'))
GC_for_sequence=[]
for i in records:
GC=0
for j in i:
if j in "GC":
GC+=1
GC_for_sequence.append(GC/len(i))
print(GC_for_sequence)
Ожидаемое выполнение будет следующим: каждый процесс занимает одну последовательность, и они выполняют статистику параллельно.
Несколько заметок о вашем существующем коде для начала:
Я бы посоветовал не делать: list (SeqIO.parse(…))
как это приостановит выполнение до тех пор, пока все последовательности не будут загружены в память, вам гораздо лучше (память и общее время выполнения), просто оставив его в качестве итератора и потребляя элементы для работников по мере необходимости
цикл по каждому символу довольно медленный, использование str.count
будет намного быстрее
сложив это вместе, вы можете сделать:
from Bio import SeqIO
with open('chr1.fa') as fd:
gc_for_sequence=[]
for seq in SeqIO.parse(fd, 'fasta'):
gc = sum(seq.seq.count(base) for base in "GC")
gc_for_sequence.append(gc / len(seq))
если это все еще не достаточно быстро, то вы можете использовать multiprocessing
модуль, например:
from Bio import SeqIO
from multiprocessing import Pool
def sequence_gc_prop(seq):
return sum(seq.count(base) for base in "GC") / len(seq)
with open('chr1.fa') as fd, Pool() as pool:
gc_for_sequence = pool.map(
sequence_gc_prop,
(seq.seq for seq in SeqIO.parse(fd, 'fasta')),
chunksize=1000,
)
комментарии от Лукаша в основном применяются. другие неочевидные вещи:
seq.seq for seq in…
это убедиться, что мы не собираем ненужные данныеchunksize
довольно большое значение, потому что функция должна быть быстрой, поэтому мы хотим дать детям разумный объем работы, чтобы родительский процесс не тратил все свое время на оркестровку вещей.Вот одна идея со стандартным многопроцессорным модулем:
from multiprocessing import Pool
import numpy as np
no_cores_to_use = 4
GC_for_sequence = [np.random.rand(100) for x in range(10)]
with Pool(no_cores_to_use) as pool:
result = pool.map(np.average, GC_for_sequence)
print(result)
В коде я использовал модуль numpy
для имитации списка с некоторым содержанием. pool.map
принимает функцию, которую вы хотите использовать в ваших данных, в качестве первого аргумента, а список данных - в качестве второго. Функция, которую вы можете легко определить самостоятельно. По умолчанию он должен принимать один аргумент. Если вы хотите передать больше, используйте functools.partial
.
[EDIT] Вот пример, гораздо ближе к вашей проблеме:
from multiprocessing import Pool
import numpy as np
records = ['ACTGTCGCAGC' for x in range(10)]
no_cores_to_use = 4
def count(sequence):
count = sequence.count('GC')
return count
with Pool(no_cores_to_use) as pool:
result = pool.map(count, records)
print(sum(result))
list (SeqIO.parse(f,'fasta'))
)), поэтому вам не нужно ничего делать.
top
одновременно, чтобы убедиться, что он работает на нескольких процессорах. в догадке: такого рода задачи не очень подходят для параллельной работы, количество полезной работы, которая может быть распределена на каждый процессор, ограничено. Это означает, что основной процесс будет тратить большую часть своего времени на чтение данных и координацию. поможет переосмысление проблемы, например, обработка нескольких файлов одновременно