编辑:Python 2.7.8
我有两个文件。 p_m有几百条包含第2列中可接受值的记录.p_t有数千万条记录,我希望确保第14列来自已提到的可接受值集。因此,在第一个while循环中,我正在读取所有可接受的值,创建一个集合(用于重复数据删除),然后将该集合转换为一个列表(我没有基准测试以查看集合是否会比列表,实际上......)。在第二个循环中,我把它尽可能地减少了几行,但是我不知道它们是否是最快的几行(我使用[14]索引两次,因为异常非常罕见,我没有''我想打扰一个变量的赋值)。目前扫描大约需要40分钟。关于如何改进的任何想法?
def contentScan(p_m,p_t):
""" """
vcont=sets.Set()
i=0
h = open(p_m,"rb")
while(True):
line = h.readline()
if not line:
break
i += 1
vcont.add(line.split("|")[2])
h.close()
vcont = list(vcont)
vcont.sort()
i=0
h = open(p_t,"rb")
while(True):
line = h.readline()
if not line:
break
i += 1
if line.split("|")[14] not in vcont:
print "%s is not defined in the matrix." %line.split("|")[14]
return 1
h.close()
print "PASS All variable_content_id values exist in the matrix." %rem
return 0
答案 0 :(得分:2)
检查几百项set
中的成员资格 比检查等效list
中的成员资格要快得多。然而,考虑到你惊人的40分钟运行时间,差异可能没那么有意义。 E.g:
ozone:~ alex$ python -mtimeit -s'a=list(range(300))' '150 in a'
100000 loops, best of 3: 3.56 usec per loop
ozone:~ alex$ python -mtimeit -s'a=set(range(300))' '150 in a'
10000000 loops, best of 3: 0.0789 usec per loop
因此,如果您使用该套装检查“数千万次”,应该可以节省数十秒 - 优于任何事情,但几乎无法衡量。
同样的考虑适用于其他非常可取的改进,例如转动循环结构:
h = open(p_t,"rb")
while(True):
line = h.readline()
if not line:
break
...
h.close()
变得更加光滑:
with open(p_t, 'rb') as h:
for line in h:
...
再次,这不会为你节省每次迭代几微秒 - 所以,比如5000万行,这不到40分钟之一。同样删除完全未使用的i += 1
- 它没有任何意义,但它采取行动将没有什么区别。
一个答案集中在split
操作的成本上。这取决于您拥有的每条记录的字段数,但是,例如:
ozone:~ alex$ python -mtimeit -s'a="xyz|"*20' 'a.split("|")[14]'
1000000 loops, best of 3: 1.81 usec per loop
所以,再说一次,这里的任何优化都可以为你节省每次迭代最多一微秒 - 再次,如果那样,另一分钟就会被削减。
真的,这里的关键问题是为什么阅读和检查例如5000万条记录应该花费40分钟--2400秒 - 每条线48微秒;毫无疑问,即使使用此处提及的所有优化以及其他答案和评论,每行仍然超过40微秒。
因此,一旦您应用了所有优化(并确认代码仍然太慢),请尝试分析该程序 - 例如http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html - 找出确切的位置所有的时间都在进行。
另外,为了确保它不仅仅是某个特别慢的磁盘的I / O,请使用大循环的内容部分“注释掉” - 只需读取大文件并且不进行任何处理或检查一切都在上面;这将告诉你什么是“不可减少的”I / O开销(如果I / O负责你经过的大部分时间,那么你就无法做很多改进,尽管将开放更改为open(thefile, 'rb', HUGE_BUFFER_SIZE)
可能帮助一点)并且可能想要考虑改进硬件设置 - 对磁盘进行碎片整理,使用本地而不是远程文件系统,等等......
答案 1 :(得分:1)
列表查找是问题(正如您所注意到的)。搜索列表的时间复杂度为 O(n),其中 n 是列表中存储的项目数。另一方面,在散列表中找到一个值(这就是python字典实际上是这样)具有 O(1)的复杂性。由于列表中有数百个项目,因此列表查找比字典查找要贵两个数量级。这与使用字典替换列表时所看到的34倍改进一致。
为了进一步缩短执行时间5-10倍,您可以使用Python JIT。我个人喜欢Pypy http://pypy.org/features.html。您无需修改脚本,只需安装pypy并运行:
pypy [your_script.py]
答案 2 :(得分:0)
根据评论,我决定尝试使用dict而不是列表来存储我可以检查大文件的可接受值(我确实密切注意.split但没有改变它)。基于将列表更改为dict,我看到了执行时间的立即和巨大改进。
使用timeit
并在一百万行文件上运行5次迭代,基于列表的检查器得到884.2秒,基于dict的检查器得到25.4秒!因此,改变2或3行的改进为34倍。
感谢大家的灵感!这是我登陆的解决方案:
def contentScan(p_m,p_t):
""" """
vcont=set()
with open(p_m,'rb') as h:
for line in h:
vcont.add(line.split("|")[2])
with open(p_t,"rb") as h:
for line in h:
if line.split("|")[14] not in vcont:
print "%s is not defined in the matrix." %line.split("|")[14]
return 1
print "PASS All variable_content_id values exist in the matrix."
return 0
答案 3 :(得分:-1)
是的,它根本不是最佳选择。 split
像地狱一样昂贵(创建新列表,创建N个字符串,将它们附加到列表中)。扫描13s“|”,扫描14s“|” (从13s pos开始)和line[pos13 + 1:pos14 - 1]
。
非常确定你可以通过这个小改动让跑步速度提高2-10倍。要添加更多 - 你不能提取字符串,而是循环通过有效的字符串,并且每个字符串都来自pos13 + 1 char,字符匹配时。如果你以“|”结尾其中一个字符串 - 这是好的。此外,它有助于在数据文件中按频率对有效字符串列表进行排序。但是,不是在每一步创建包含数十个字符串的列表更为重要。
以下是您的测试:
无法使其格式化代码,所以这里是要点。
https://gist.github.com/iced/16fe907d843b71dd7b80
测试条件:带有2核和1GB RAM的VBox运行ubuntu 14.10及最新更新。每次变化都执行了10次,每次运行前重启VBox并抛弃最低和最高运行时间。
结果: no_op:25.025 原文:26.964 no_split:25.102
原创 - no_op:1.939 no_split - no_op:0.077
虽然在这种情况下,这种特殊的优化是无用的,因为大部分时间都花在了IO上。我无法找到测试设置,使IO低于70%。在任何情况下 - 拆分都很昂贵,在不需要时应该避免。
PS。是的,据我所知,即使是1K good items
,使用散列也会更好(实际上,当散列计算比loookup更快时可能更好 - 可能是100个元素),我的观点是分裂在这种情况下是昂贵的。