Подтвердить что ты не робот

Самый эффективный способ в Python для итерации по большому файлу (10 ГБ +)

Я работаю над Python script, чтобы пройти через два файла - один из которых содержит список UUID, а другой содержит большое количество записей журнала - каждая строка содержит один из UUID из другого файла. Цель программы - создать список UUIDS из файла1, а затем каждый раз, когда UUID будет найден в файле журнала, увеличьте связанное значение для каждого момента, когда будет найдено совпадение.

Короче говоря, подсчитайте, сколько раз каждый UUID появляется в файле журнала. На данный момент у меня есть список, который заполняется UUID в качестве ключа, а "hits" - как значение. Затем еще один цикл, который итерации по каждой строке файла журнала, и проверка того, соответствует ли 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" выполняется быстрее, чем использование скомпилированного регулярного выражения. Я думал, что чтение файлов в кусках, а не по строкам, приведет к повышению производительности за счет сокращения объема времени ввода-вывода диска, но разница в производительности тестового файла ~ 200 МБ была бы небрежной. Если бы у кого-то были другие методы, я был бы очень благодарен:)

4b9b3361

Ответ 1

Думайте функционально!

  • Напишите функцию, которая возьмет строку файла журнала и вернет uuid. Назовите его uuid, скажем.

  • Применить эту функцию к каждой строке файла журнала. Если вы используете Python 3, вы можете использовать встроенную карту функций; в противном случае вам нужно использовать itertools.imap.

  • Передайте этот итератор коллекции .Counter.

    collections.Counter(map(uuid, open("log.txt")))
    

Это будет практически оптимально эффективным.

Несколько комментариев:

  • Это полностью игнорирует список UUID и просто подсчитывает те, которые отображаются в файле журнала. Вам потребуется немного изменить программу, если вы этого не хотите.

    • Ваш код медленный, потому что вы используете неправильные структуры данных. Здесь вам нужен дикт.

Ответ 2

Как говорят выше люди, с 10-гигабайтным файлом вы, скорее всего, попадете в пределы своего диска довольно быстро. Для улучшения кода только советы генератора великолепны. В 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 

Ответ 3

Вы пробовали mincemeat.py? Это реализация Python MapReduce распределенной вычислительной среды. Я не уверен, что у вас будет увеличение производительности, так как я еще не обработал 10 ГБ данных перед его использованием, хотя вы можете изучить эту структуру.

Ответ 4

Это не 5-строчный ответ на ваш вопрос, но был отличный учебник, приведенный в PyCon'08 под названием Генерирующие трюки для системных программистов. Существует также учебное пособие под названием Любопытный курс по Corouts и Concurrency.

Учебник Generator специально использует в качестве примера большую обработку файлов журналов.

Ответ 5

Попробуйте измерить, сколько времени потрачено, используя профайлер http://docs.python.org/library/profile.html

Где лучше всего оптимизировать, зависит от характера ваших данных: если список uuids не очень длинный, вы можете найти, например, что большая часть времени тратится на "если logFunc.progress( lineCount, logSize)". Если список очень длинный, вы можете помочь сохранить результат uidHits.keys() для переменной вне цикла и перебрать ее вместо самого словаря, но Рош Оксюморон предлагает сначала найти идентификатор, а затем проверить его в uidHits, вероятно, поможет еще больше.

В любом случае вы можете исключить переменную lineCount и вместо этого использовать i. И find(uid) != -1 может быть лучше, чем count(uid) == 1, если строки очень длинные.