Python中最有效的迭代大文件的方式(10GB +)

时间:2011-06-02 13:56:21

标签: python file design-patterns matching

我正在编写一个Python脚本来浏览两个文件 - 一个包含UUID列表,另一个包含大量日志条目 - 每行包含一个来自另一个文件的UUID。该程序的目的是从file1创建UUIDS列表,然后每次在日志文件中找到UUID时,每次找到匹配时都会增加相关值。

故事很长,请计算每个UUID在日志文件中出现的次数。 目前,我有一个列表,其中填充了UUID作为键,并且“点击”作为值。然后是另一个循环,它迭代日志文件的每一行,并检查日志中的UUID是否与UUID列表中的UUID匹配。如果匹配,则递增该值。

    for i, logLine in enumerate(logHandle):         #start matching UUID entries in log file to UUID from rulebase
        if logFunc.progress(lineCount, logSize):    #check progress
            print logFunc.progress(lineCount, logSize)  #print progress in 10% intervals
        for uid in uidHits:
            if logLine.count(uid) == 1:             #for each UUID, check the current line of the log for a match in the UUID list
                uidHits[uid] += 1                   #if matched, increment the relevant value in the uidHits list
                break                                #as we've already found the match, don't process the rest
        lineCount += 1               

它可以正常工作 - 但我确信有一种更有效的方法来处理文件。我经历了一些指南,发现使用'count'比使用编译的正则表达式更快。我认为以块为单位而不是逐行读取文件可以通过减少磁盘I / O时间来提高性能,但测试文件~200MB的性能差异可以忽略不计。如果有人有任何其他方法,我将非常感激:)

5 个答案:

答案 0 :(得分:14)

从功能上思考!

  1. 编写一个函数,它将获取日志文件的一行并返回uuid。称之为uuid,比如说。

  2. 将此函数应用于日志文件的每一行。如果您使用的是Python 3,则可以使用内置函数映射;否则,你需要使用itertools.imap。

  3. 将此迭代器传递给collections.Counter。

    collections.Counter(map(uuid, open("log.txt")))
    
  4. 这将非常有效。

    一对评论:

    • 这完全忽略了UUID列表,只计算出现在日志文件中的UUID。如果你不想要这个,你需要稍微修改一下程序。

      • 您的代码很慢,因为您使用了错误的数据结构。 dict就是你想要的。

答案 1 :(得分:4)

就像上面的人说的那样,使用10GB的文件你可能会很快达到磁盘的极限。对于仅代码改进,生成器建议很棒。在python 2.x中,它看起来像

uuid_generator = (line.split(SPLIT_CHAR)[UUID_FIELD] for line in file)

听起来这实际上并不是一个python问题。如果你没有做比计算UUID更复杂的事情,Unix可能能够比python更快地解决你的问题。

cut -d${SPLIT_CHAR} -f${UUID_FIELD} log_file.txt | sort | uniq -c 

答案 2 :(得分:3)

你试过mincemeat.py吗?它是MapReduce分布式计算框架的Python实现。我不确定你是否会获得性能提升,因为我在使用它之前还没有处理过10GB的数据,尽管你可能会探索这个框架。

答案 3 :(得分:3)

这不是你问题的5行答案,但在PyCon'08上有一个名为Generator Tricks for System Programmers的优秀教程。还有一个名为A Curious Course on Coroutines and Concurrency的后续教程。

Generator教程专门以大日志文件处理为例。

答案 4 :(得分:0)

使用分析器http://docs.python.org/library/profile.html

尝试测量花费大部分时间的位置

最佳优化将取决于数据的性质:如果uuids列表不是很长,您可能会发现,例如,大部分时间花在“if logFunc.progress”上( lineCount,logSize)“。如果列表 非常长,那么它可以帮助将uidHits.keys()的结果保存到循环外的变量并迭代它而不是字典本身,但Rosh Oxymoron建议找到id首先然后在uidHits中检查它可能会有所帮助。

在任何情况下,您都可以删除lineCount变量,然后使用i。如果行很长,find(uid) != -1可能会优于count(uid) == 1