[英]How to speed up this Python code?
我有一个以下微小的Python方法,它是迄今为止性能热点(根据我的分析器,在这里花费了大约95%的执行时间)在一个更大的程序中:
def topScore(self, seq):
ret = -1e9999
logProbs = self.logProbs # save indirection
l = len(logProbs)
for i in xrange(len(seq) - l + 1):
score = 0.0
for j in xrange(l):
score += logProbs[j][seq[j + i]]
ret = max(ret, score)
return ret
代码是在Python的Jython实现中运行,而不是CPython,如果这很重要的话。 seq
是DNA序列串,大约1,000个元素。 logProbs
是一个字典列表,每个位置一个。 目标是找到seq
的任何长度l
(大约10-20个元素)子序列的最大分数。
我意识到所有这些循环由于解释开销而效率低下,并且在静态编译/ JIT语言中会更快。 但是,我不愿意切换语言。 首先,我需要一个JVM语言用于我正在使用的库,这种约束我的选择。 其次,我不想将此代码批量转换为较低级别的JVM语言。 但是,如果有必要,我愿意用其他东西重写这个热点,虽然我不知道如何连接它或者开销是多少。
除了这种方法的单线程缓慢之外,我还无法让程序在并行化方面超过4个CPU。 鉴于它几乎所有的时间都花在我发布的10行热点上,我无法弄清楚这里的瓶颈是什么。
它缓慢的原因是因为它是O(N * N)
最大子序列算法可以帮助您改进这一点
如果针对相同的seq
重复调用topScore
则可以memoize
其值。
我不知道我在做什么,但也许这可以帮助加快你的算法:
ret = -1e9999
logProbs = self.logProbs # save indirection
l = len(logProbs)
scores = collections.defaultdict(int)
for j in xrange(l):
prob = logProbs[j]
for i in xrange(len(seq) - l + 1):
scores[i] += prob[seq[j + i]]
ret = max(ret, max(scores.values()))
那么在for i循环之外预先计算xrange(l)
呢?
没有什么能像慢一样跳出来。 我可能会像这样重写内部循环:
score = sum(logProbs[j][seq[j+i]] for j in xrange(l))
甚至:
seqmatch = zip(seq[i:i+l], logProbs)
score = sum(posscores[base] for base, posscores in seqmatch)
但我不知道要么节省很多时间。
将DNA碱基存储为整数0-3可能稍微快一些,并从元组而不是字典中查找分数。 将字母翻译成数字会有性能影响,但只需要进行一次。
绝对使用numpy并将logProbs存储为2D数组而不是字典列表。 如上所述,还将seq存储为(短)整数的一维数组。 如果您不必在每次调用函数时都进行这些转换,这将有所帮助(在函数内部进行这些转换不会为您节省太多)。 你可以消除第二个循环:
import numpy as np
...
print np.shape(self.logProbs) # (20, 4)
print np.shape(seq) # (1000,)
...
def topScore(self, seq):
ret = -1e9999
logProbs = self.logProbs # save indirection
l = len(logProbs)
for i in xrange(len(seq) - l + 1):
score = np.sum(logProbs[:,seq[i:i+l]])
ret = max(ret, score)
return ret
之后你做了什么取决于这两个数据元素中哪一个最常变化:
如果logProbs通常保持不变并且您希望通过它运行许多DNA序列,那么请考虑将DNA序列堆叠为2D阵列。 numpy可以非常快速地遍历2D阵列,因此如果您要处理200个DNA序列,它只需要比单个序列长一点。
最后,如果你真的需要加速,请使用scipy.weave。 这是编写几行快速C以加速循环的一种非常简单的方法。 但是,我建议scipy> 0.8。
您可以尝试在循环外提升不仅仅是self.logProbs:
def topScore(self, seq):
ret = -1e9999
logProbs = self.logProbs # save indirection
l = len(logProbs)
lrange = range(l)
for i in xrange(len(seq) - l + 1):
score = 0.0
for j in lrange:
score += logProbs[j][seq[j + i]]
if score > ret: ret = score # avoid lookup and function call
return ret
我怀疑它会产生重大影响,但你可以尝试改变:
for j in xrange(l):
score += logProbs[j][seq[j + i]]
至
for j,lP in enumerate(logProbs):
score += lP[seq[j + i]]
甚至在seq循环外提升枚举。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.