如何用python(FASTA的GC计算器)并行化这段代码?
说到编程,我是个新手。我已经读完了《面向生物学家的实用计算》一书,现在正在研究一些稍微高级一些的概念 我编写了一个Python(2.7)脚本,它读取.fasta文件并计算GC内容。代码如下所示 我正在处理的文件很麻烦(~3.9GB),我想知道是否有一种方法可以利用多个处理器,或者是否值得花点时间。我有一个四核(超线程)英特尔I-7 2600K处理器 我运行了代码并查看了系统资源(附图片),以查看CPU上的负载。这个进程CPU有限吗?是IO有限公司吗?这些概念对我来说是很新的。我使用了多处理模块和Pool(),但没有用(可能是因为我的函数返回了一个元组) 代码如下:如何用python(FASTA的GC计算器)并行化这段代码?,python,python-multiprocessing,dna-sequence,Python,Python Multiprocessing,Dna Sequence,说到编程,我是个新手。我已经读完了《面向生物学家的实用计算》一书,现在正在研究一些稍微高级一些的概念 我编写了一个Python(2.7)脚本,它读取.fasta文件并计算GC内容。代码如下所示 我正在处理的文件很麻烦(~3.9GB),我想知道是否有一种方法可以利用多个处理器,或者是否值得花点时间。我有一个四核(超线程)英特尔I-7 2600K处理器 我运行了代码并查看了系统资源(附图片),以查看CPU上的负载。这个进程CPU有限吗?是IO有限公司吗?这些概念对我来说是很新的。我使用了多处理模块和
def GC_calc(InFile):
Iteration = 0
GC = 0
Total = 0
for Line in InFile:
if Line[0] != ">":
GC = GC + Line.count('G') + Line.count('C')
Total = Total + len(Line)
Iteration = Iteration + 1
print Iteration
GCC = 100 * GC / Total
return (GC, Total, GCC)
InFileName = "WS_Genome_v1.fasta"
InFile = open(InFileName, 'r')
results = GC_calc(InFile)
print results
目前,代码的主要瓶颈是
打印迭代
。打印到标准输出非常慢。如果您删除这一行,或者至少,如果您确实需要它,请将它移到另一个线程,我希望性能会有很大的提升。然而,线程管理是一个高级主题,我建议现在不要讨论它
另一个可能的瓶颈是从文件中读取数据。文件IO可能很慢,尤其是当您的机器上只有一个HDD时。对于单个HDD,您根本不需要使用多处理,因为您无法为处理器核心提供足够的数据。面向性能的RAID和SSD在这方面可以有所帮助
最后一点是尝试使用grep
和类似的文本操作程序来代替python。他们经过了几十年的优化,有很好的机会更快地工作。在这方面有很多问题,grep
优于python。或者,在将数据传递给脚本之前,至少可以使用FASTA头:
$ grep "^[>]" WS_Genome_v1.fasta | python gc_calc.py
(取自
grep“^[>]”
)在这种情况下,您不应该打开脚本中的文件,而是应该从sys.stdin
中读取行,就像您现在这样做一样。基本上,您在计算每行中的C
和G
的数量,并计算行的长度。
只有在最后你才能计算出总数
这样的过程很容易并行进行,因为每条线的计算是独立的
假设计算是在CPython(来自python.org的)中完成的,threading
不会因为GIL而大大提高性能
这些计算可以与多处理.Pool
并行进行。
进程不像线程那样共享数据。我们不想将3.9GB文件的一部分发送到每个工作进程!
因此,您希望每个辅助进程自己打开文件。操作系统的缓存应该注意同一文件中的页面不会多次加载到内存中
如果您有N个核,我将创建worker函数,以便处理每个第N行,并带有偏移量
def worker(arguments):
n = os.cpu_count() + 1
infile, offset = arguments
with open(infile) as f:
cg = 0
totlen = 0
count = 1
for line in f:
if (count % n) - offset == 0:
if not line.startswith('>'):
cg += line.count('C') +
line.count('G')
totlen += len(line)
count += 1
return (cg, totlen)
你可以这样管理游泳池
import multiprocessing as mp
from os import cpu_count
pool = mp.Pool()
results = pool.map(worker, [('infile', n) for n in range(1, cpu_count()+1)])
默认情况下,池
创建的工人数量与CPU的核心数量相同
results
将是一个(cg,len)元组列表,您可以轻松地求和
编辑:更新以修复模零错误。我现在有了一个用于并行化的工作代码(特别感谢@Roland Smith)。我只需要对代码做两个小的修改,还有一个关于.fasta文件的结构的警告。最终(工作)代码如下:
###ONLY WORKS WHEN THERE ARE NO BREAKS IN SEQUENCE LINES###
def GC_calc(arguments):
n = mp.cpu_count()
InFile, offset = arguments
with open(InFile) as f:
GC = 0
Total = 0
count = 0
for Line in f:
if (count % n) - offset == 0:
if Line[0] != ">":
Line = Line.strip('\n')
GC += Line.count('G') + Line.count('C')
Total += len(Line)
count += 1
return (GC, Total)
import time
import multiprocessing as mp
startTime = time.time()
pool = mp.Pool()
results = pool.map(GC_calc, [('WS_Genome_v2.fasta', n) for n in range(1, mp.cpu_count()+1)])
endTime = time.time()
workTime = endTime - startTime
#Takes the tuples, parses them out, adds them
GC_List = []
Tot_List = []
# x = GC count, y = total count: results = [(x,y), (x,y), (x,y),...(x,y)]
for x,y in results:
GC_List.append(x)
Tot_List.append(y)
GC_Final = sum(GC_List)
Tot_Final = sum(Tot_List)
GCC = 100*float(GC_Final)/float(Tot_Final)
print results
print
print "Number GC = ", GC_Final
print "Total bp = ", Tot_Final
print "GC Content = %.3f%%" % (GCC)
print
endTime = time.time()
workTime = endTime - startTime
print "The job took %.5f seconds to complete" % (workTime)
需要注意的是.fasta文件本身在序列中不能有中断。我的原始代码并没有问题,但当序列被分成多行时,这段代码将无法正常工作。这非常简单,可以通过命令行进行修复
我还必须在两个地方修改代码:
n=mp.cpu\u计数()
及
计数=0
最初,count设置为1,n设置为mp.cpu_count()+1。这导致计数不准确,即使在文件更正之后也是如此。缺点是它还允许所有8个内核(好吧,线程)工作。新代码只允许4在任何给定时间工作
但是它确实把这个过程从23秒加速到了13秒!因此,我认为这是一次成功(除了更正原始.fasta文件所需的时间)。如果您正在读取同一文件,多处理将不会对您有所帮助,因为您几乎肯定会受到硬盘读取速度的限制。另外,请关闭您的文件。。。python的
with
关键字非常有助于确保文件在处理完毕后关闭。但是,如果您希望获得多处理方面的帮助只是为了学习,您必须发布实际涉及并行处理的代码,以便我们能够帮助您。如果您实际上没有任何错误,您可能会在@Aaron上找到更多的运气啊,是的,原始代码确实有一个close命令-忘了这里!非常感谢。我可能会尝试使用pool()函数生成一些代码(重新创建它),但当我尝试使用pool.map将函数拆分为4个进程时,确实收到了一条错误消息。问题在于我如何尝试分割输入(它不喜欢我使用pool.map函数和我的函数,并尝试分割我的_文件/4)。我在遵循一个生成随机数据的示例,然后以这种方式分割这些数据(如上),所以我的方法显然不合适。这非常有趣!我将尝试一下并比较性能。我确实使用grep提取了文件的另一个版本(删除了“>”行和重复序列),但没有想到将其直接传输到脚本。打印语句可以让我跟踪进度和速度,而无需完成整个过程,但我可以看到这可能会减慢速度@坎菲斯-这个q