如何让这个Python文件扫描速度更快?

时间:2015-03-13 15:51:08

标签: python file optimization

编辑: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

4 个答案:

答案 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)

编辑:制作更多的pythony。 编辑2:使用set builtin而不是dict。

根据评论,我决定尝试使用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,字符匹配时。如果你以“|”结尾其中一个字符串 - 这是好的。此外,它有助于在数据文件中按频率对有效字符串列表进行排序。但是,不是在每一步创建包含数十个字符串的列表更为重要。

以下是您的测试:

  1. 生成器(ts - 您可以调整它以使我们获得一些真实数据)。
  2. no-op(只是阅读)
  3. 原始解决方案。
  4. 没有拆分解决方案
  5. 无法使其格式化代码,所以这里是要点。

    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个元素),我的观点是分裂在这种情况下是昂贵的。