Проблемы скорости обработки текстового файла Python

У меня есть проблема с обработкой largeish файла в Python. Все, что я делаю,

f = gzip.open(pathToLog, 'r')
for line in f:
        counter = counter + 1
        if (counter % 1000000 == 0):
                print counter
f.close

Это берет вокруг 10m25 s только, чтобы открыть файл, считать строки и увеличить этот счетчик.

В жемчуге, имея дело с тем же файлом и делая вполне немного больше (некоторый материал регулярного выражения), целый процесс берет вокруг 1m17 s.

Код Perl:

open(LOG, "/bin/zcat $logfile |") or die "Cannot read $logfile: $!\n";
while (<LOG>) {
        if (m/.*\[svc-\w+\].*login result: Successful\.$/) {
                $_ =~ s/some regex here/$1,$2,$3,$4/;
                push @an_array, $_
        }
}
close LOG;

Кто-либо может советовать тому, что я могу сделать для создания решения Python выполненным на подобной скорости к решению для Perl?

РЕДАКТИРОВАНИЕ, которое я попытался просто распаковать файл и иметь дело с ним с помощью открытого вместо gzip.open, но который только изменяет общее время приблизительно на 4m14.972 s, который является все еще слишком медленным.

Я также удалил и операторы печати по модулю и заменил их передачей, таким образом, все, что делается теперь, перемещается от файла до файла.

9
задан Anonymouslemming 23 April 2010 в 07:46
поделиться

4 ответа

В Python (как минимум <= 2.6.x) синтаксический анализ формата gzip реализован в Python (поверх zlib). Более того, похоже, что он делает некоторые странные вещи, а именно, распаковывает до конца файла в память, а затем отбрасывает все, что превышает запрошенный размер чтения (затем делает это снова для следующего чтения). ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ : Я только что просмотрел gzip.read () в течение 3 минут, поэтому могу ошибаться. Независимо от моего понимания gzip.read () верен или нет, модуль gzip не оптимизирован для больших объемов данных. Попробуйте сделать то же самое, что и в Perl, т.е. запустить внешний процесс (например, см. Подпроцесс модуля ).


РЕДАКТИРОВАТЬ На самом деле, я пропустил замечание ОП о том, что ввод-вывод простого файла столь же медленен, как и сжатый (спасибо ire_and_curses за указание на это). Это показалось мне маловероятным, поэтому я провел некоторые измерения ...

from timeit import Timer

def w(n):
    L = "*"*80+"\n"
    with open("ttt", "w") as f:
        for i in xrange(n) :
            f.write(L)

def r():
    with open("ttt", "r") as f:
        for n,line in enumerate(f) :
            if n % 1000000 == 0 :
                print n

def g():
    f = gzip.open("ttt.gz", "r")
    for n,line in enumerate(f) :
        if n % 1000000 == 0 :
        print n

Теперь, запустив ...

>>> Timer("w(10000000)", "from __main__ import w").timeit(1)
14.153118133544922
>>> Timer("r()", "from __main__ import r").timeit(1)
1.6482770442962646
# here i switched to a terminal and made ttt.gz from ttt
>>> Timer("g()", "from __main__ import g").timeit(1)

... и после перерыва на чай, обнаружив, что он все еще работает, я убил его, извините . Затем я попробовал 100 000 строк вместо 10 000 000:

>>> Timer("w(100000)", "from __main__ import w").timeit(1)
0.05810999870300293
>>> Timer("r()", "from __main__ import r").timeit(1)
0.09662318229675293
# here i switched to a terminal and made ttt.gz from ttt
>>> Timer("g()", "from __main__ import g").timeit(1)
11.939290046691895

Время модуля gzip равно O (file_size ** 2), поэтому при количестве строк порядка миллионов время чтения gzip не может быть таким же, как обычное время чтения (как мы видим, подтверждено экспериментом). Anonymouslemming, проверьте еще раз.

9
ответ дан 4 December 2019 в 13:00
поделиться

Если вы погуглите, "почему python gzip медленный", вы найдете множество обсуждений этого вопроса, включая исправления для улучшений в Python 2.7 и 3.2. А пока используйте zcat, как вы делали это в Perl, который очень быстро работает. Ваша (первая) функция занимает у меня около 4,19 секунды со сжатым файлом размером 5 МБ, а вторая функция занимает 0,78 секунды. Однако я не знаю, что происходит с вашими несжатыми файлами. Если я распакую файлы журналов (журналы apache) и запустил на них две функции с простым Python open (file) и Popen ('cat'), Python будет быстрее (0,17 с), чем cat (0,48 с).

#!/usr/bin/python

import gzip
from subprocess import PIPE, Popen
import sys
import timeit

#pathToLog = 'big.log.gz' # 50M compressed (*10 uncompressed)
pathToLog = 'small.log.gz' # 5M ""

def test_ori():
    counter = 0
    f = gzip.open(pathToLog, 'r')
    for line in f:
        counter = counter + 1
        if (counter % 100000 == 0): # 1000000
            print counter, line
    f.close

def test_new():
    counter = 0
    content = Popen(["zcat", pathToLog], stdout=PIPE).communicate()[0].split('\n')
    for line in content:
        counter = counter + 1
        if (counter % 100000 == 0): # 1000000
            print counter, line

if '__main__' == __name__:
    to = timeit.Timer('test_ori()', 'from __main__ import test_ori')
    print "Original function time", to.timeit(1)

    tn = timeit.Timer('test_new()', 'from __main__ import test_new')
    print "New function time", tn.timeit(1)
5
ответ дан 4 December 2019 в 13:00
поделиться

Ваш компьютер занял 10 минут? Это должно быть ваше оборудование. Я написал эту функцию для записи 5 миллионов строк:

def write():
    fout = open('log.txt', 'w')
    for i in range(5000000):
        fout.write(str(i/3.0) + "\n")
    fout.close

Затем я прочитал ее с помощью программы, очень похожей на вашу:

def read():
    fin = open('log.txt', 'r')
    counter = 0
    for line in fin:
        counter += 1
        if counter % 1000000 == 0:
            print counter
    fin.close

Моему компьютеру потребовалось около 3 секунд, чтобы прочитать все 5 миллионов строк.

0
ответ дан 4 December 2019 в 13:00
поделиться

Я потратил некоторое время на это. Надеюсь, этот код поможет. Он использует zlib и не использует внешние вызовы.

Метод gunzipchunks считывает сжатый файл gzip частями, которые можно повторять (генератор).

Метод gunziplines считывает эти несжатые фрагменты и предоставляет вам одну строку за раз, которую также можно повторять (другой генератор).

Наконец, метод gunziplinescounter дает вам то, что вы ищете.

Ура!

import zlib

file_name = 'big.txt.gz'
#file_name = 'mini.txt.gz'

#for i in gunzipchunks(file_name): print i
def gunzipchunks(file_name,chunk_size=4096):
    inflator = zlib.decompressobj(16+zlib.MAX_WBITS)
    f = open(file_name,'rb')
    while True:
        packet = f.read(chunk_size)
        if not packet: break
        to_do = inflator.unconsumed_tail + packet
        while to_do:
            decompressed = inflator.decompress(to_do, chunk_size)
            if not decompressed:
                to_do = None
                break
            yield decompressed
            to_do = inflator.unconsumed_tail
    leftovers = inflator.flush()
    if leftovers: yield leftovers
    f.close()

#for i in gunziplines(file_name): print i
def gunziplines(file_name,leftovers="",line_ending='\n'):
    for chunk in gunzipchunks(file_name): 
        chunk = "".join([leftovers,chunk])
        while line_ending in chunk:
            line, leftovers = chunk.split(line_ending,1)
            yield line
            chunk = leftovers
    if leftovers: yield leftovers

def gunziplinescounter(file_name):
    for counter,line in enumerate(gunziplines(file_name)):
        if (counter % 1000000 != 0): continue
        print "%12s: %10d" % ("checkpoint", counter)
    print "%12s: %10d" % ("final result", counter)
    print "DEBUG: last line: [%s]" % (line)

gunziplinescounter(file_name)

Это должно работать намного быстрее, чем использование встроенного модуля gzip для очень больших файлов.

2
ответ дан 4 December 2019 в 13:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: