我为生物过程编写了一个python程序https://codereview.stackexchange.com/questions/186396/solve-the-phase-state-between-two-haplotype-blocks-using-markov-transition-proba。
如果您查看该程序,您可以看到该程序一次需要花费大量时间来计算来自两个连续行(或键,值)的数据。我不是把整个代码放在这里,但为了简单起见,我创建了一个模拟文件和模拟程序(如下所示),其行为类似于最简单的级别。在这个模拟程序中,我正在计算,说len(vals)
列并将其写回输出文件。
由于在原始程序(上面的链接)中执行for (k1, v1) and (k2, v2) ....
时计算是CPU / GPU绑定,我想通过 多处理/线程化数据分析 - 1) 以最快的方式读取内存中的整个数据 2)通过唯一chr
字段将数据划分为块 3)执行计算 4)将其写回文件。 那么,我该怎么做?
在给定的模拟文件中,计算过于简单而无法受GPU / CPU限制,但我只是想知道如果需要我该怎么做。
注意:我有太多人问我想要实现的目标 - 我正在尝试多处理/解决给定的问题。如果我把我原来的整个大项目放在这里,没有人会去看它。所以,让我们来训练这个小文件和小python程序。
以下是我的代码和数据:
my_data = '''chr\tpos\tidx\tvals
2\t23\t4\tabcd
2\t25\t7\tatg
2\t29\t8\tct
2\t35\t1\txylfz
3\t37\t2\tmnost
3\t39\t3\tpqr
3\t41\t6\trtuv
3\t45\t5\tlfghef
3\t39\t3\tpqr
3\t41\t6\trtu
3\t45\t5\tlfggg
4\t25\t3\tpqrp
4\t32\t6\trtu
4\t38\t5\tlfgh
4\t51\t3\tpqr
4\t57\t6\trtus
'''
def manipulate_lines(vals):
vals_len = len(vals[3])
return write_to_file(vals[0:3], vals_len)
def write_to_file(a, b):
print(a,b)
to_file = open('write_multiprocessData.txt', 'a')
to_file.write('\t'.join(['\t'.join(a), str(b), '\n']))
to_file.close()
def main():
to_file = open('write_multiprocessData.txt', 'w')
to_file.write('\t'.join(['chr', 'pos', 'idx', 'vals', '\n']))
to_file.close()
data = my_data.rstrip('\n').split('\n')
for lines in data:
if lines.startswith('chr'):
continue
else:
lines = lines.split('\t')
manipulate_lines(lines)
if __name__ == '__main__':
main()
答案 0 :(得分:1)
使用多个进程处理数据时要处理的问题是保留顺序。 Python提出了一种相当不错的处理方法,使用multiprocessing.Pool
,可用于map
输入数据上的进程。然后,这将按顺序返回结果。
但是,处理可能仍然无序,因此要正确使用它,只应处理,并且不应在子进程中运行IO访问。因此,要在您的情况下使用它,需要执行一小段代码重写,其中所有IO操作都在主进程中发生:
from multiprocessing import Pool
from time import sleep
from random import randint
my_data = '''chr\tpos\tidx\tvals
2\t23\t4\tabcd
2\t25\t7\tatg
2\t29\t8\tct
2\t35\t1\txylfz
3\t37\t2\tmnost
3\t39\t3\tpqr
3\t41\t6\trtuv
3\t45\t5\tlfghef
3\t39\t3\tpqr
3\t41\t6\trtu
3\t45\t5\tlfggg
4\t25\t3\tpqrp
4\t32\t6\trtu
4\t38\t5\tlfgh
4\t51\t3\tpqr
4\t57\t6\trtus
'''
def manipulate_lines(vals):
sleep(randint(0, 2))
vals_len = len(vals[3])
return vals[0:3], vals_len
def write_to_file(a, b):
print(a,b)
to_file = open('write_multiprocessData.txt', 'a')
to_file.write('\t'.join(['\t'.join(a), str(b), '\n']))
to_file.close()
def line_generator(data):
for line in data:
if line.startswith('chr'):
continue
else:
yield line.split('\t')
def main():
p = Pool(5)
to_file = open('write_multiprocessData.txt', 'w')
to_file.write('\t'.join(['chr', 'pos', 'idx', 'vals', '\n']))
to_file.close()
data = my_data.rstrip('\n').split('\n')
lines = line_generator(data)
results = p.map(manipulate_lines, lines)
for result in results:
write_to_file(*result)
if __name__ == '__main__':
main()
此程序不会在其不同的chr
值之后拆分列表,而是直接从列表中以最大5(参数Pool
)子流程处理条目。
为了显示数据仍处于预期的顺序,我向manipulate_lines
函数添加了随机睡眠延迟。这显示了这个概念,但可能无法给出正确的加速视图,因为休眠进程允许另一个进程并行运行,而计算量大的进程将在其所有运行时使用CPU。
可以看出,一旦map
调用返回,就会完成对文件的写入,这确保所有子进程都已终止并返回其结果。在场景后面进行这种通信需要相当多的开销,因此为了有益,计算部分必须比写入阶段长得多,并且它不能生成太多数据来写入文件
此外,我还打破了生成器中的for
循环。这样可以根据要求提供multiprocessing.Pool
的输入。另一种方法是预处理data
列表,然后将该列表直接传递给Pool
。我发现生成器解决方案更好,并且具有更小的峰值内存消耗。
另外,关于多线程与多处理的评论;只要你进行计算繁重的操作,就应该使用多处理,至少在理论上,它允许进程在不同的机器上运行。此外,在cPython中 - 最常用的Python实现 - 线程遇到另一个问题,即全局解释器锁(GIL)。这意味着一次只能执行一个线程,因为解释器会阻止所有其他线程的访问。 (有一些例外,例如当使用用C编写的模块时,比如numpy。在这些情况下,GIL可以在进行numpy计算时释放,但通常情况并非如此。)因此,线程主要用于程序的情况等待缓慢,无序,IO等等。 (套接字,终端输入等)
答案 1 :(得分:-2)
我只使用了几次线程,我没有在下面测试过这段代码,但是从快速浏览一下,for循环实际上是唯一可以从线程中受益的地方。
我会让其他人决定。
{{1}}