我需要读取千兆字节的文本,所以我正在尝试优化我的代码。这样做时,我发现,对于我的问题,使用字典比if-tests更快。
check = {'R':'-', 'F':'+'}
seqs = ['R', 'F']*100
def check1():
for entry in seqs:
if entry == 'R':
strand = '-'
if entry == 'F':
strand = '+'
def check2():
for entry in seqs:
strand = check[entry]
使用ipythong的%timeit我发现在字典中查找的速度比使用两个if-test快两倍:
In [63]: %timeit check1()
10000 loops, best of 3: 38.8 us per loop
In [64]: %timeit check2()
100000 loops, best of 3: 16.2 us per loop
由于if-tests是如此基本,我没想到性能差异。这是众所周知的吗?任何人都可以解释为什么会这样吗?
更新
我检查了上面的两个函数以及下面的check3()如何影响我实际代码的运行时间,并且对总时间没有影响。因此,在现实世界的例子中,要么字典中的提升不是那么高,其中'R'和'F'值需要不断地从文件中重新读取,或者这段代码不是我的瓶颈的一部分
无论如何,谢谢你的答案!
答案 0 :(得分:7)
您实际上并未证明在字典中查找比两次if
测试更快。您所展示的是,查找特定字典比这两个测试更快。
通常,字典查找需要几个步骤:从密钥生成哈希以找到潜在匹配,然后通过比较密钥来测试潜在匹配。如果存在哈希表冲突,有时可能需要进行多次比较。如果你有用户定义的键类,那么这两个步骤都可能很慢,它们通常对字符串很快,但在一个特殊情况下它们真的非常快,你已经遇到了这种情况。
您的词典使用的短字符串与编译时已知的标识符格式相匹配。 Python将有助于“实践”你的字符串'R'和'F'。由于您在测试中使用的字符串在编译时也是已知的,因此它们将是完全相同的实例。对字典查找的所有这些意味着,查找的专用版本用于仅具有字符串键的字典,哈希始终是预先计算的,并且通过比较地址来进行密钥比较(至少在成功时和您的它永远不会失败的两把钥匙。)
你的真实代码,我假设是从输入读取字符串,所以它不会有'R'的实习副本。这意味着它需要计算每行输入的哈希值。地址不匹配,因此必须为每个测试调用字符串比较函数。你仍然只对字符串键进行一些优化,至少它不必对可能不是字符串的对象进行通用比较。
if
语句对对象类型一无所知,因此每次都会进行通用比较。
答案 1 :(得分:4)
与许多VM代码一样,它主要归结为所涉及的VM操作码数量。
您可以使用dis
检查汇编的函数:
import dis
dis.dis(func)
在2.6.4中,对于每个比较和分支,check1需要大约15-20个操作码(取决于代码路径)。 check2只需7(在添加缺少的chedict
字典后,全局声明)。
答案 2 :(得分:1)
字典在Python中进行了大量优化;查询是O(1)
- 它只是一个哈希表查找,因此只是一个“操作” - 使用if
测试序列(O(n)
)获得的操作数量的一半。
答案 3 :(得分:1)
这会揭示一些事情:
def check3():
for entry in seqs:
if entry == 'R':
strand = '-'
else:
strand = '+'
它实际上比我计算机上的check2()
快。