Итак, у меня есть несколько довольно гигантских файлов .gz - мы говорим от 10 до 20 Гб каждый при распаковке.

Мне нужно перебрать каждую из них, поэтому я использую стандарт:

import gzip
f = gzip.open(path+myFile, 'r')
for line in f.readlines():
    #(yadda yadda)
f.close()

Однако обе команды open() и close() принимают AGES, используя 98% памяти + ЦП. Настолько, что программа выходит и печатает Killed в терминал. Может быть, он загружает весь извлеченный файл в память?

Я сейчас использую что-то вроде:

from subprocess import call
f = open(path+'myfile.txt', 'w')
call(['gunzip', '-c', path+myfile], stdout=f)
#do some looping through the file
f.close()
#then delete extracted file

Это работает. Но есть ли более чистый путь?

17
LittleBobbyTables 2 Фев 2013 в 02:18

2 ответа

Лучший ответ

Я на 99% уверен, что ваша проблема не в gzip.open(), а в readlines().

Как документация объясняет:

f.readlines () возвращает список, содержащий все строки данных в файле.

Очевидно, что для этого требуется чтение, чтение и распаковка всего файла, а также создание абсолютно гигантского списка.

Скорее всего, на самом деле это malloc вызовы, чтобы выделить всю эту память, которая занимает вечно. И затем, в конце этой области (при условии, что вы используете CPython), он должен собрать весь этот гигантский список, который также будет длиться вечно.

Вы почти никогда не хотите использовать readlines. Если вы не используете очень старый Python, просто сделайте это:

for line in f:

file итеративно полон строк, как list, возвращаемый readlines - за исключением того, что на самом деле это не list, он генерирует больше строк на лету, читая из буфера. Таким образом, в любой момент времени у вас будет только одна строка и пара буферов порядка 10 МБ каждая вместо 25 ГБ list. И чтение и распаковка будут распространяться на весь цикл, а не делаться все сразу.

Из быстрого теста с gzip-файлом объемом 3,5 ГБ, gzip.open() фактически мгновенный, for line in f: pass занимает несколько секунд, gzip.close() эффективно мгновенный. Но если я это сделаю for line in f.readlines(): pass, это займет ... ну, я не уверен, как долго, потому что примерно через минуту моя система вошла в ужасный ад свопинга, и мне пришлось принудительно убить переводчика, чтобы заставить его реагировать на что-нибудь…


Поскольку с момента получения этого ответа это повторялось еще десятки раз, я написал это сообщение в блоге что объясняет немного больше.

59
abarnert 16 Июл 2013 в 19:10

Ознакомьтесь с пандами, в частности инструментами ввода-вывода. Они поддерживают сжатие gzip при чтении файлов, и вы можете читать файлы кусками. Кроме того, панды очень быстрые и эффективные для памяти.

Поскольку я никогда не пробовал, я не знаю, насколько хорошо совместимы сжатие и чтение в блоках, но, возможно, стоит попробовать

2
jalanb 30 Авг 2017 в 16:18